home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / VBSamples / Misc / DXSetup / setup1.bas < prev    next >
Encoding:
BASIC Source File  |  2001-10-08  |  198.4 KB  |  5,375 lines

  1. Attribute VB_Name = "basSetup1"
  2. Option Explicit
  3. Option Compare Text
  4. '
  5. 'Public Constants
  6. '
  7.  
  8. Public Enum OverwriteReturnVal
  9.     owYes
  10.     owNo
  11.     owNoToAll
  12. End Enum
  13.  
  14. Public Enum FileComparison
  15.     fcOlder
  16.     fcEquivalent
  17.     fcNewer
  18. End Enum
  19.  
  20. 'DirectX Setup Install constants
  21. Private Const DSETUP_DDRAWDRV As Long = 8
  22. Private Const DSETUP_DSOUNDDRV As Long = 16
  23. Private Const DSETUP_DXCORE As Long = 65536
  24. Private Const DSETUP_DIRECTX As Long = (DSETUP_DXCORE Or DSETUP_DDRAWDRV Or DSETUP_DSOUNDDRV)
  25. Private Const DSETUP_TESTINSTALL As Long = 131072
  26. Private Const DSETUP_NTINSTALL As Long = 524288
  27. Private Const DSETUPERR_SUCCESS_RESTART As Long = 1
  28. Private Const DSETUPERR_SUCCESS As Long = 0
  29. Private Const DSETUP_VERSION As Long = &H40000
  30.  
  31. 'DirectX Setup Install routines
  32. Private Declare Function DirectXSetup Lib "dsetup.dll" Alias "DirectXSetupA" (ByVal hWnd As Long, ByVal lpszRootPath As String, ByVal dwFlags As Long) As Long
  33. Private Declare Function DirectXSetupGetVersion Lib "dsetup.dll" (dwVersion As Long, dwRevision As Long) As Long
  34.  
  35. 'Return values for setup toolkit functions
  36. Public Const gintRET_CONT% = 1
  37. Public Const gintRET_CANCEL% = 2
  38. Public Const gintRET_EXIT% = 3
  39. Public Const gintRET_ABORT% = 4
  40. Public Const gintRET_FATAL% = 5
  41. Public Const gintRET_FINISHEDSUCCESS% = 6 'Used only as parameter to ExitSetup at end of successful install
  42.  
  43. 'Error levels for GetAppRemovalCmdLine()
  44. Public Const APPREMERR_NONE = 0 'no error
  45. Public Const APPREMERR_FATAL = 1 'fatal error
  46. Public Const APPREMERR_NONFATAL = 2 'non-fatal error, user chose to abort
  47. Public Const APPREMERR_USERCANCEL = 3 'user chose to cancel (no error)
  48.  
  49. 'Beginning of lines in [Files], [Bootstrap], and [Licenses] sections of SETUP.LST
  50. Public Const gstrINI_FILE$ = "File"
  51. Public Const gstrINI_REMOTE$ = "Remote"
  52. Public Const gstrINI_LICENSE$ = "License"
  53. '
  54. ' Command line constants
  55. '
  56. 'These should remain lowercase
  57. Public Const gstrSILENTSWITCH = "s"
  58. #If SMS Then
  59. Public Const gstrSMSSWITCH = "q"
  60. #End If
  61. '
  62. ' Icon Information
  63. '
  64. Public Const gsGROUP As String = "Group"
  65. Public Const gsICON As String = "Icon"
  66. Public Const gsTITLE As String = "Title"
  67. Public Const gsICONGROUP As String = "IconGroups"
  68.  
  69. Public Const gstrINI_BOOTFILES$ = "Bootstrap Files"
  70.  
  71. 'Font info
  72. 'These should remain uppercase
  73. Public Const gsEXT_FONTTTF As String = "TTF"
  74. Public Const gsEXT_FONTFON As String = "FON"
  75. Private Declare Function AddFontResource Lib "gdi32" Alias "AddFontResourceA" (ByVal lpFilename As String) As Long
  76.  
  77. 'Registry files (execute them based on .reg extension)
  78. Public Const gsREGEDIT As String = "regedit /s "
  79. Public Const gsEXT_REG As String = "reg"
  80. '
  81. 'Type Definitions
  82. '
  83. Public Type FILEINFO                                        ' Setup information file line format
  84.     intDiskNum As Integer                                   ' disk number
  85.     fDestDirRecognizedBySetupExe As Boolean                 ' Does setup.exe recognize this destination directory macro?
  86.     strSrcName As String                                    ' name of source file
  87.     strDestName As String                                   ' name of destination file
  88.     strDestDir As String                                    ' destination directory
  89.     strRegister As String                                   ' registration info
  90.     fShared As Boolean                                      ' whether the file is shared or private
  91.     fSystem As Boolean                                      ' whether the file is a system file (i.e. should be installed but never removed)
  92.     varDate As Date                                         ' file date
  93.     lFileSize As Long                                       ' file size
  94.     sVerInfo As VERINFO                                     ' file version number
  95.     strReserved As String                                   ' Reserved. Leave empty, or error.
  96.     strProgramIconTitle As String                                ' Caption for icon in program group
  97.     strProgramIconCmdLine As String                         ' Command Line for icon in program group
  98. End Type
  99.  
  100. Public Type DISKINFO                                        ' Disk drive information
  101.     lAvail As Long                                          ' Bytes available on drive
  102.     lReq As Long                                            ' Bytes required for setup
  103.     lMinAlloc As Long                                       ' minimum allocation unit
  104. End Type
  105.  
  106. Public Type DESTINFO                                        ' save dest dir for certain files
  107.     strAppDir As String
  108.     strAUTMGR32 As String
  109.     strRACMGR32 As String
  110. End Type
  111.  
  112. Public Type REGINFO                                         ' save registration info for files
  113.     strFilename As String
  114.     strRegister As String
  115.     
  116.     'The following are used only for remote server registration
  117.     strNetworkAddress As String
  118.     strNetworkProtocol As String
  119.     intAuthentication As Integer
  120.     fDCOM As Boolean      ' True if DCOM, otherwise False
  121. End Type
  122.  
  123. '
  124. 'Public Variables
  125. '
  126. Public gstrSETMSG As String
  127. Public gintRetVal As Integer                                  'return value for form based functions
  128. Public gstrAppName As String                                'name of app being installed
  129. Public gintCabs As Long
  130. Public gstrTitle As String                                  '"setup" name of app being installed
  131. Public gstrDestDir As String                                'dest dir for application files
  132. Public gstrAppExe As String                                 'name of app .EXE being installed
  133. Public gstrAppToUninstall As String                         ' Name of app exe/ocx/dll to be uninstalled.  Should be the same as gstrAppExe in most cases.
  134. Public gstrSrcPath As String                                'path of source files
  135. Public gstrSetupInfoFile As String                          'pathname of SETUP.LST file
  136. Public gstrWinDir As String                                 'windows directory
  137. Public gstrFontDir As String                                'windows\font directory
  138. Public gstrWinSysDir As String                              'windows\system directory
  139. Public gsDiskSpace() As DISKINFO                            'disk space for target drives
  140. Public gcolDrivesUsed As Collection                         'dest drives used by setup
  141. Public glTotalCopied As Long                                'total bytes copied so far
  142. Public gintCurrentDisk As Integer                           'current disk number being installed
  143. Public gsDest As DESTINFO                                   'dest dirs for certain files
  144. Public gstrAppRemovalLog As String                           'name of the app removal logfile
  145. Public gstrAppRemovalEXE As String                           'name of the app removal executable
  146. Public gfAppRemovalFilesMoved As Boolean                     'whether or not the app removal files have been moved to the application directory
  147. Public gfForceUseDefDest As Boolean                         'If set to true, then the user will not be prompted for the destination directory
  148. Public fMainGroupWasCreated As Boolean                     'Whether or not a main folder/group has been created
  149. Public gfRegDAO As Boolean                                 ' If this gets set to true in the code, then
  150.                                                            ' we need to add some registration info for DAO
  151.                                                            ' to the registry.
  152.  
  153. Public gfDXReboot As Boolean                                ' we need to reboot because of DirectX
  154. Public gsCABFULLNAME As String
  155. Public gsTEMPDIR As String
  156.  
  157. Public Const gsINI_CABNAME As String = "Cab"
  158. Public Const gsINI_TEMPDIR As String = "TmpDir"
  159. '
  160. 'Form/Module Constants
  161. '
  162.  
  163. 'SetFileTime junk
  164. Public Type FileTime
  165.     dwLowDateTime As Long
  166.     dwHighDateTime As Long
  167. End Type
  168. Public Type SYSTEMTIME
  169.     wYear As Integer
  170.     wMonth As Integer
  171.     wDayOfWeek As Integer
  172.     wDay As Integer
  173.     wHour As Integer
  174.     wMinute As Integer
  175.     wSecond As Integer
  176.     wMilliseconds As Integer
  177. End Type
  178.  
  179. Public Const GENERIC_WRITE As Long = &H40000000
  180. Public Const GENERIC_READ As Long = &H80000000
  181. Public Const FILE_ATTRIBUTE_NORMAL As Long = &H80
  182. Public Const FILE_FLAG_WRITE_THROUGH As Long = &H80000000
  183. Public Const OPEN_EXISTING As Long = 3
  184. Public Const INVALID_HANDLE_VALUE As Long = -1
  185. Public Const ERROR_SHARING_VIOLATION As Long = 32
  186.  
  187. Public Declare Function LocalFileTimeToFileTime Lib "Kernel32" (lpFileTime As FileTime, lpLocalFileTime As FileTime) As Long
  188. Public Declare Function CreateFile Lib "Kernel32" Alias "CreateFileA" (ByVal lpFilename As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
  189. Public Declare Function SetFileTime Lib "Kernel32" (ByVal hFile As Long, lpCreationTime As FileTime, lpLastAccessTime As FileTime, lpLastWriteTime As FileTime) As Long
  190. Public Declare Function CloseHandle Lib "Kernel32" (ByVal hObject As Long) As Long
  191. Public Declare Function SystemTimeToFileTime Lib "Kernel32" (lpSystemTime As SYSTEMTIME, lpFileTime As FileTime) As Long
  192. Public Declare Function VariantChangeTypeEx Lib "oleaut32.dll" (ByVal pvArgDest As Long, ByVal pvArgSrc As Long, ByVal LCID As Long, ByVal wFlags As Integer, ByVal VarType As Integer) As Long
  193. Public Declare Function VariantTimeToSystemTime Lib "oleaut32.dll" (ByVal vtime As Date, lpSystemTime As SYSTEMTIME) As Long
  194.  
  195. 'Special file names
  196. Private Const mstrFILE_APPREMOVALLOGBASE$ = "ST6UNST"               'Base name of the app removal logfile
  197. Private Const mstrFILE_APPREMOVALLOGEXT$ = ".LOG"                   'Default extension for the app removal logfile
  198. 'These should remain uppercase
  199. Private Const mstrFILE_AUTMGR32 = "AUTMGR32.EXE"
  200. Private Const mstrFILE_RACMGR32 = "RACMGR32.EXE"
  201. Private Const mstrFILE_RICHED32$ = "RICHED32.DLL"
  202.  
  203. 'setup information file registration macros
  204. 'These should remain uppercase
  205. Private Const mstrDLLSELFREGISTER$ = "$(DLLSELFREGISTER)"
  206. Private Const mstrEXESELFREGISTER$ = "$(EXESELFREGISTER)"
  207. Private Const mstrTLBREGISTER$ = "$(TLBREGISTER)"
  208. Private Const mstrREMOTEREGISTER$ = "$(REMOTE)"
  209. Private Const mstrVBLREGISTER$ = "$(VBLREGISTER)"  ' Bug 5-8039
  210.  
  211. '
  212. 'Form/Module Variables
  213. '
  214. Private msRegInfo() As REGINFO                                  'files to be registered
  215. Private mlTotalToCopy As Long                                   'total bytes to copy
  216. Private mstrVerTmpName As String                                'temp file name for VerInstallFile API
  217.  
  218. ' Hkey cache (used for logging purposes)
  219. Private Type HKEY_CACHE
  220.     hKey As Long
  221.     strHkey As String
  222. End Type
  223.  
  224. Private hkeyCache() As HKEY_CACHE
  225.  
  226. ' Registry manipulation API's (32-bit)
  227. Public Const HKEY_CLASSES_ROOT = &H80000000
  228. Public Const HKEY_CURRENT_USER = &H80000001
  229. Public Const HKEY_LOCAL_MACHINE = &H80000002
  230. Public Const HKEY_USERS = &H80000003
  231. Private Const ERROR_SUCCESS = 0&
  232. Private Const ERROR_NO_MORE_ITEMS = 259&
  233.  
  234. Private Const REG_SZ = 1
  235. Private Const REG_BINARY = 3
  236. Private Const REG_DWORD = 4
  237.  
  238.  
  239. Private Declare Function OSRegCloseKey Lib "advapi32" Alias "RegCloseKey" (ByVal hKey As Long) As Long
  240. Private Declare Function OSRegCreateKey Lib "advapi32" Alias "RegCreateKeyA" (ByVal hKey As Long, ByVal lpszSubKey As String, phkResult As Long) As Long
  241. Private Declare Function OSRegDeleteKey Lib "advapi32" Alias "RegDeleteKeyA" (ByVal hKey As Long, ByVal lpszSubKey As String) As Long
  242. Private Declare Function OSRegEnumKey Lib "advapi32" Alias "RegEnumKeyA" (ByVal hKey As Long, ByVal iSubKey As Long, ByVal lpszName As String, ByVal cchName As Long) As Long
  243. Private Declare Function OSRegOpenKey Lib "advapi32" Alias "RegOpenKeyA" (ByVal hKey As Long, ByVal lpszSubKey As String, phkResult As Long) As Long
  244. Private Declare Function OSRegQueryValueEx Lib "advapi32" Alias "RegQueryValueExA" (ByVal hKey As Long, ByVal lpszValueName As String, ByVal dwReserved As Long, lpdwType As Long, lpbData As Any, cbData As Long) As Long
  245. Private Declare Function OSRegSetValueEx Lib "advapi32" Alias "RegSetValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal Reserved As Long, ByVal dwType As Long, ByVal lpData As String, ByVal cbData As Long) As Long
  246. Private Declare Function OSRegSetValueNumEx Lib "advapi32" Alias "RegSetValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal Reserved As Long, ByVal dwType As Long, lpData As Any, ByVal cbData As Long) As Long
  247.  
  248. Public Declare Sub lstrcpyn Lib "Kernel32" (ByVal strDest As String, ByVal strSrc As Any, ByVal lBytes As Long)
  249. Private Declare Function GetCurrentProcessId Lib "Kernel32" () As Long
  250. Public Declare Function ExtractFileFromCab Lib "vb6stkit.dll" (ByVal Cab As String, ByVal File As String, ByVal Dest As String, ByVal iCab As Long, ByVal sSrc As String) As Long
  251. 'Reboot info
  252. Public Const ANYSIZE_ARRAY = 1
  253.  
  254. Private Type LARGE_INTEGER
  255.     lowpart As Long
  256.     highpart As Long
  257. End Type
  258.  
  259. Private Type LUID_AND_ATTRIBUTES
  260.     pLuid As LARGE_INTEGER
  261.     Attributes As Long
  262. End Type
  263.  
  264. Private Type TOKEN_PRIVILEGES
  265.     PrivilegeCount As Long
  266.     Privileges(ANYSIZE_ARRAY) As LUID_AND_ATTRIBUTES
  267. End Type
  268.  
  269. Public Const TOKEN_ADJUST_PRIVILEGES = 32
  270. Public Const TOKEN_QUERY = 8
  271. Public Const SE_PRIVILEGE_ENABLED As Long = 2
  272.  
  273. Private Declare Function LookupPrivilegeValue Lib "advapi32.dll" Alias "LookupPrivilegeValueA" (ByVal lpSystemName As String, ByVal lpName As String, lpLuid As LARGE_INTEGER) As Long
  274. Private Declare Function GetCurrentProcess Lib "Kernel32" () As Long
  275. Private Declare Function AdjustTokenPrivileges Lib "advapi32.dll" (ByVal TokenHandle As Long, ByVal DisableAllPrivileges As Long, NewState As TOKEN_PRIVILEGES, ByVal BufferLength As Long, PreviousState As TOKEN_PRIVILEGES, ReturnLength As Long) As Long
  276. Private Declare Function OpenProcessToken Lib "advapi32.dll" (ByVal ProcessHandle As Long, ByVal DesiredAccess As Long, TokenHandle As Long) As Long
  277. 'Exit the program and return an error code
  278. Private Declare Sub ExitProcess Lib "Kernel32" (ByVal uExitCode As Long)
  279.  
  280. Private mfDontAskOnSpaceErr As Boolean
  281.  
  282. Private Const msDRIVE_INDEX_SEPARATOR As String = "|"
  283.  
  284. '-----------------------------------------------------------
  285. ' SUB: AddPerAppPath
  286. '
  287. ' Adds an application's full pathname and per-app path to the
  288. '   system registry (this is currently only meaningful to
  289. '   Windows 95).
  290. '
  291. ' IN: [strAppExe] - app EXE name, not including path
  292. '     [strAppDir] - full path of EXE, not including filename
  293. '     [strAppPath] - per-app path for this application
  294. '       (semicolon-separated list of directory path names)
  295. '       If this is the empty string, no per-app path
  296. '       is registered, but the full pathname of the
  297. '       exe IS still registered.
  298. '
  299. ' OUT:
  300. '   Example registry entries:
  301. '     HKEY_LOCAL_MACHINE\[strPathsBaseKeyName]\MyApp.Exe
  302. '       [Default]=C:\Program Files\MyApp\MyApp.Exe
  303. '       [Path]=C:\Program Files\MyApp;C:\Program Files\MyApp\System
  304. '-----------------------------------------------------------
  305. '
  306. Public Sub AddPerAppPath(ByVal strAppExe As String, ByVal strAppDir As String, ByVal strPerAppPath As String)
  307.     Dim strPathsBaseKeyName As String
  308.     Const strAppPaths$ = "App Paths"
  309.     Const strAppPathKeyName = "Path"
  310.     Dim fOk As Boolean
  311.     Dim hKey As Long
  312.     
  313.     AddDirSep strAppDir
  314.     
  315.     ' Create the new key, whose name is based on the app's name
  316.     If Not RegCreateKey(HKEY_LOCAL_MACHINE, RegPathWinCurrentVersion, strAppPaths & gstrSEP_DIR & strAppExe, hKey) Then
  317.         GoTo Err
  318.     End If
  319.     
  320.     fOk = True
  321.     
  322.     ' Default value indicates full EXE pathname
  323.     fOk = fOk And RegSetStringValue(hKey, vbNullString, strAppDir & strAppExe)
  324.     
  325.     ' [Path] value indicates the per-app path
  326.     If Len(strPerAppPath) > 0 Then
  327.         fOk = fOk And RegSetStringValue(hKey, strAppPathKeyName, strPerAppPath)
  328.     End If
  329.     
  330.     If Not fOk Then
  331.         GoTo Err
  332.     End If
  333.     
  334.     RegCloseKey hKey
  335.     
  336.     Exit Sub
  337.     
  338. Err:
  339.     RegCloseKey hKey
  340.  
  341.     MsgError ResolveResString(resERR_REG), vbExclamation Or vbOKOnly, gstrTitle
  342.     '
  343.     ' If we are running an SMS install, we can't continue.
  344.     '
  345. #If SMS Then
  346.     If gfSMS Then
  347.         ExitSetup frmSetup1, gintRET_FATAL
  348.     End If
  349. #End If
  350. End Sub
  351.  
  352. '-----------------------------------------------------------
  353. ' FUNCTION: AddQuotesToFN
  354. '
  355. ' Given a pathname (directory and/or filename), returns
  356. '   that pathname surrounded by double quotes if the
  357. '   path contains spaces or commas.  This is required for
  358. '   setting up an icon correctly, since otherwise such paths
  359. '   would be interpreted as a pathname plus arguments.
  360. '-----------------------------------------------------------
  361. '
  362. Private Function AddQuotesToFN(ByVal strFilename) As String
  363.     If InStr(strFilename, " ") Or InStr(strFilename, ",") Then
  364.         AddQuotesToFN = gstrQUOTE & strFilename & gstrQUOTE
  365.     Else
  366.         AddQuotesToFN = strFilename
  367.     End If
  368. End Function
  369.  
  370. '-----------------------------------------------------------
  371. ' SUB: CalcDiskSpace
  372. '
  373. ' Calculates disk space required for installing the files
  374. ' listed in the specified section of the setup information
  375. ' file (SETUP.LST)
  376. '-----------------------------------------------------------
  377. '
  378. Public Sub CalcDiskSpace(ByVal strSection As String)
  379.     Dim lDestFileSpace As Long
  380.  
  381.     Dim intIdx As Integer
  382.     Dim intDrvIdx As Integer
  383.     Dim sFile As FILEINFO
  384.     Dim strDrive As String
  385.     Dim lThisFileSpace As Long
  386.     Dim sDestFile As String
  387.  
  388.     intIdx = 1
  389.  
  390.     On Error GoTo CalcDSError
  391.  
  392.     '
  393.     'For each file in the specified section, read info from the setup info file
  394.     '
  395.     Do While ReadSetupFileLine(strSection, intIdx, sFile)
  396.         '
  397.         'Get the dest drive used for this file.  If this is the first file using
  398.         'the drive for a destination, add the drive to the drives used 'table',
  399.         'allocate an array element for the holding the drive info, and get
  400.         'available disk space and minimum allocation unit
  401.         '
  402.         GetDrive sFile.strDestDir, strDrive
  403.         intDrvIdx = DriveIndexFromDrive(strDrive)
  404.  
  405.         '
  406.         'Calculate size of the dest final (file size + minimum allocation for drive)
  407.         '
  408.         lThisFileSpace = CalcFinalSize(sFile.lFileSize, strDrive)
  409.         mlTotalToCopy = mlTotalToCopy + lThisFileSpace
  410.  
  411.         '
  412.         'If the file already exists, then if we copy it at all, we'll be
  413.         'replacing it.  So, we get the size of the existing dest file so
  414.         'that we can subtract it from the amount needed later.
  415.         '
  416.         sDestFile = sFile.strDestDir & sFile.strDestName
  417.         If FileExists(sDestFile) Then
  418.             lDestFileSpace = FileLen(sDestFile)
  419.         Else
  420.             lDestFileSpace = 0
  421.         End If
  422.  
  423.         '
  424.         'Subtract size of existing dest file, if applicable and then accumulate
  425.         'space required
  426.         '
  427.         lThisFileSpace = lThisFileSpace - lDestFileSpace
  428.         If lThisFileSpace < 0 Then
  429.             lThisFileSpace = 0
  430.         End If
  431.  
  432.         gsDiskSpace(intDrvIdx).lReq = gsDiskSpace(intDrvIdx).lReq + lThisFileSpace
  433.  
  434.         intIdx = intIdx + 1
  435.     Loop
  436.  
  437.     Exit Sub
  438.  
  439. CalcDSError:
  440.     MsgError Err.Description & vbLf & vbLf & ResolveResString(resCALCSPACE), vbCritical, gstrSETMSG
  441.     ExitSetup frmMessage, gintRET_FATAL
  442. End Sub
  443.  
  444. '-----------------------------------------------------------
  445. ' SUB: CalcFinalSize
  446. '
  447. ' Computes the space required for a file of the size
  448. ' specified on the given dest path.  This includes the
  449. ' file size plus a padding to ensure that the final size
  450. ' is a multiple of the minimum allocation unit for the
  451. ' dest drive
  452. '-----------------------------------------------------------
  453. '
  454. Private Function CalcFinalSize(lBaseFileSize As Long, strDestPath As String) As Long
  455.     Dim lMinAlloc As Long
  456.     Dim intPadSize As Long
  457.  
  458.     lMinAlloc = gsDiskSpace(DriveIndexFromPath(strDestPath)).lMinAlloc
  459.     intPadSize = lMinAlloc - (lBaseFileSize Mod lMinAlloc)
  460.     If intPadSize = lMinAlloc Then
  461.         intPadSize = 0
  462.     End If
  463.  
  464.     CalcFinalSize = lBaseFileSize + intPadSize
  465. End Function
  466.  
  467. '-----------------------------------------------------------
  468. ' FUNCTION: DriveIndexFromPath
  469. '
  470. ' Given a path, returns the index of the corresponding
  471. ' drive. Adds the drive if not yet encountered.
  472. '-----------------------------------------------------------
  473. '
  474. Public Function DriveIndexFromPath(Path As String) As Integer
  475.     Dim sDrive As String
  476.  
  477.     GetDrive Path, sDrive
  478.     DriveIndexFromPath = DriveIndexFromDrive(sDrive)
  479. End Function
  480.  
  481. '-----------------------------------------------------------
  482. ' FUNCTION: DriveIndexFromDrive
  483. '
  484. ' Given a drive, returns the index. Adds the drive if not
  485. ' yet encountered.
  486. '-----------------------------------------------------------
  487. '
  488. Public Function DriveIndexFromDrive(Drive As String) As Integer
  489.     Dim nIndex As Integer
  490.  
  491.     On Error Resume Next
  492.     SplitDriveData (gcolDrivesUsed.Item(Drive)), , nIndex
  493.     On Error GoTo 0
  494.     If nIndex = 0 Then
  495.         nIndex = AddDrive(Drive)
  496.     End If
  497.     DriveIndexFromDrive = nIndex
  498. End Function
  499.  
  500. '-----------------------------------------------------------
  501. ' FUNCTION: DriveFromDriveIndex
  502. '
  503. ' Given an index, returns the drive.
  504. '-----------------------------------------------------------
  505. '
  506. Public Function DriveFromDriveIndex(Index As Integer) As String
  507.     Dim sDrive As String
  508.  
  509.     SplitDriveData (gcolDrivesUsed.Item(Index)), sDrive
  510.     DriveFromDriveIndex = sDrive
  511. End Function
  512.  
  513. '-----------------------------------------------------------
  514. ' SUB: SplitDriveData
  515. '
  516. ' Outputs the index and string from the data for a
  517. ' particular drive.
  518. '-----------------------------------------------------------
  519. '
  520. Private Sub SplitDriveData(Data As String, Optional ByRef Drive As String, Optional ByRef Index As Integer)
  521.     Dim nSepPos As Long
  522.     Dim sData As String
  523.  
  524.     nSepPos = InStr(1, Data, msDRIVE_INDEX_SEPARATOR)
  525.     Index = Val(Left$(Data, nSepPos - 1))
  526.     Drive = Mid$(Data, nSepPos + Len(msDRIVE_INDEX_SEPARATOR))
  527. End Sub
  528.  
  529. '-----------------------------------------------------------
  530. ' FUNCTION: AddDrive
  531. '
  532. ' Adds a drive to the list of drives, and returns the
  533. ' corresponding index. Note: The Drive parameter must always
  534. ' be the output from GetDrive.
  535. '-----------------------------------------------------------
  536. '
  537. Public Function AddDrive(Drive As String) As Long
  538.     Dim nIndex As Integer
  539.     Dim sValue As String
  540.  
  541.     nIndex = gcolDrivesUsed.Count + 1
  542.     sValue = nIndex & msDRIVE_INDEX_SEPARATOR & Drive
  543.     gcolDrivesUsed.Add sValue, Drive
  544.     ReDim Preserve gsDiskSpace(nIndex)
  545.     gsDiskSpace(nIndex).lAvail = GetDiskSpaceFree(Drive)
  546.     gsDiskSpace(nIndex).lMinAlloc = GetDrivesAllocUnit(Drive)
  547.     AddDrive = nIndex
  548. End Function
  549.  
  550. '-----------------------------------------------------------
  551. ' FUNCTION: DriveCount
  552. '
  553. ' Returns the number of drives in our global list.
  554. '-----------------------------------------------------------
  555. '
  556. Public Function DriveCount() As Integer
  557.     DriveCount = gcolDrivesUsed.Count
  558. End Function
  559.  
  560. '-----------------------------------------------------------
  561. ' SUB: CenterForm
  562. '
  563. ' Centers the passed form just above center on the screen
  564. '-----------------------------------------------------------
  565. '
  566. Public Sub CenterForm(frm As Form)
  567.     SetMousePtr vbHourglass
  568.  
  569.     frm.Top = (Screen.Height * 0.85) \ 2 - frm.Height \ 2
  570.     frm.Left = Screen.Width \ 2 - frm.Width \ 2
  571.  
  572.     SetMousePtr vbDefault
  573. End Sub
  574. '-----------------------------------------------------------
  575. ' SUB: UpdateDateTime
  576. '
  577. ' Updates the date/time for bootstrap files
  578. '-----------------------------------------------------------
  579. '
  580. Private Sub UpdateDateTime()
  581.     Dim intIdx As Integer
  582.     Dim sFile As FILEINFO
  583.     Dim lTime As FileTime
  584.     Dim hFile As Long
  585.     '
  586.     'For each file in the specified section, read info from the setup info file
  587.     '
  588.     intIdx = 1
  589.     Do While ReadSetupFileLine(gstrINI_BOOTFILES, intIdx, sFile)
  590.         Dim sCurDate As String, sFileDate As String
  591.         
  592.         sFileDate = Format$(FileDateTime(sFile.strDestDir & sFile.strDestName), "m/d/yyyy h:m")
  593.         sCurDate = Format$(Now, "m/d/yyyy h:m")
  594.  
  595.         If sFileDate = sCurDate Then
  596.             lTime = GetFileTime(sFile.varDate)
  597.             hFile = CreateFile(sFile.strDestDir & sFile.strDestName, GENERIC_WRITE Or GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
  598.             SetFileTime hFile, lTime, lTime, lTime
  599.             DoEvents
  600.             CloseHandle hFile
  601.         End If
  602.         intIdx = intIdx + 1
  603.     Loop
  604.     
  605. End Sub
  606.  
  607. '-----------------------------------------------------------
  608. ' FUNCTION: CheckDiskSpace
  609. '
  610. ' Reads from the space required array generated by calling
  611. ' the 'CalcDiskSpace' function and determines whether there
  612. ' is sufficient free space on all of the drives used for
  613. ' installation
  614. '
  615. ' Returns: True if there is enough space, False otherwise
  616. '-----------------------------------------------------------
  617. '
  618. Public Function CheckDiskSpace() As Boolean
  619.     Dim intIdx As Integer
  620.     Dim intTmpDrvIdx As Integer
  621.     Dim lDiskSpaceLeft As Long
  622.     Dim lMostSpaceLeft As Long
  623.  
  624.     '
  625.     'Default to True (enough space on all drives)
  626.     '
  627.     CheckDiskSpace = True
  628.  
  629.     '
  630.     'For each drive that is the destination for one or more files, compare
  631.     'the space available to the space required.
  632.     '
  633.     For intIdx = 1 To DriveCount
  634.         lDiskSpaceLeft = gsDiskSpace(intIdx).lAvail - gsDiskSpace(intIdx).lReq
  635.         If lDiskSpaceLeft < 0 Then
  636.             If Not CheckDSAskSpace Then
  637.                 CheckDiskSpace = False
  638.                 Exit Function
  639.             End If
  640.         Else
  641.             '
  642.             'If no "TMP" drive was found, or if the "TMP" drive wasn't ready,
  643.             'save the index of the drive and the amount of space on the drive
  644.             'which will have the most free space.
  645.             '
  646.             If lDiskSpaceLeft > lMostSpaceLeft Then
  647.                 lMostSpaceLeft = lDiskSpaceLeft
  648.                 intTmpDrvIdx = intIdx
  649.             End If
  650.         End If
  651.     Next
  652.  
  653.     If intTmpDrvIdx > 0 Then
  654.         If gsDiskSpace(intTmpDrvIdx).lAvail < gsDiskSpace(intTmpDrvIdx).lReq Then
  655.             CheckDiskSpace = CheckDSAskSpace
  656.         End If
  657.     End If
  658. End Function
  659.  
  660. '-----------------------------------------------------------
  661. ' FUNCTION: CheckDiskSpace
  662. '
  663. ' Reads from the space required array generated by calling
  664. ' the 'CalcDiskSpace' function and determines whether there
  665. ' is sufficient free space on all of the drives used for
  666. ' installation
  667. '
  668. ' Returns: True if there is enough space, False otherwise
  669. '-----------------------------------------------------------
  670. '
  671. Private Function CheckDSAskSpace() As Boolean
  672.     '
  673.     'if the user hasn't been prompted before in the event of not enough free space,
  674.     'then display table of drive space and allow them to (basically) abort, retry,
  675.     'or ignore.
  676.     '
  677.     If Not mfDontAskOnSpaceErr Then
  678.         If gfNoUserInput Then
  679.             If gfSilent Then
  680.                 LogSilentMsg ResolveResString(resLBLNOSPACE)
  681.             End If
  682. #If SMS Then
  683.             If gfSMS Then
  684.                 LogSMSMsg ResolveResString(resLBLNOSPACE)
  685.             End If
  686. #End If
  687.             ExitSetup frmSetup1, gintRET_FATAL
  688.         Else
  689.             frmDskSpace.Show vbModal
  690.         End If
  691.  
  692.         If gintRetVal <> gintRET_CONT Then
  693.             Exit Function
  694.         Else
  695.             mfDontAskOnSpaceErr = True
  696.         End If
  697.     End If
  698.     CheckDSAskSpace = True
  699. End Function
  700.  
  701. '-----------------------------------------------------------
  702. ' FUNCTION: CheckDrive
  703. '
  704. ' Check to see if the specified drive is ready to be read
  705. ' from.  In the case of a drive that holds removable media,
  706. ' this would mean that formatted media was in the drive and
  707. ' that the drive door was closed.
  708. '
  709. ' IN: [strDrive] - drive to check
  710. '     [strCaption] - caption if the drive isn't ready
  711. '
  712. ' Returns: True if the drive is ready, False otherwise
  713. '-----------------------------------------------------------
  714. '
  715. Public Function CheckDrive(ByVal strDrive As String, ByVal strCaption As String) As Boolean
  716.     Dim strDir As String
  717.     Dim strMsg As String
  718.     Dim strNewDrive As String
  719.  
  720.     On Error Resume Next
  721.  
  722.     SetMousePtr vbHourglass
  723.  
  724.     GetDrive strDrive, strNewDrive
  725.     Do
  726.         '
  727.         'Attempt to read the current directory of the specified drive. If
  728.         'an error occurs, we assume that the drive is not ready
  729.         '
  730.         Err.Clear
  731.         strDir = Dir$(strNewDrive)
  732.         If Err.Number = 0 Then
  733.             CheckDrive = True
  734.             Exit Do
  735.         Else
  736.             If IsUNCName(strDrive) Then
  737.                 strMsg = Err.Description & vbLf & vbLf & ResolveResString(resCANTREADUNC, gstrPIPE1, strDrive) & vbLf & vbLf & ResolveResString(resCHECKUNC)
  738.             Else
  739.                 strMsg = Err.Description & vbLf & vbLf & ResolveResString(resDRVREAD) & strDrive & vbLf & vbLf & ResolveResString(resDRVCHK)
  740.             End If
  741.             If MsgError(strMsg, vbExclamation Or vbRetryCancel, strCaption) = vbCancel Then
  742.                 'The user wants to cancel, so fall out of the loop and return
  743.                 '   False implicitly.
  744.                 'CheckDrive = False (implicit)
  745.                 Exit Do
  746.             End If
  747.             'In the NoUserInput case, MsgError above will return the default
  748.             '   button value, which is vbRetry. Rather than retrying, we want to
  749.             '   exit.
  750.             If gfNoUserInput Then
  751.                 ExitSetup frmSetup1, gintRET_FATAL
  752.             End If
  753.         End If
  754.     Loop
  755.  
  756.     SetMousePtr vbDefault
  757.  
  758.     Err.Clear
  759. End Function
  760.  
  761. '-----------------------------------------------------------
  762. ' FUNCTION: CopyFile
  763. '
  764. ' Uses the Windows VerInstallFile API to copy a file from
  765. ' the specified source location/name to the destination
  766. ' location/name.
  767. ' If the file is successfully updated and the file is a
  768. ' shared file (fShared = True), then the
  769. ' files reference count is updated (32-bits only)
  770. '
  771. ' IN: [strSrcDir] - directory where source file is located
  772. '     [strDestDir] - destination directory for file
  773. '     [strSrcName] - name of source file
  774. '     [strDestName] - name of destination file
  775. '
  776. ' PRECONDITION: NewAction() must have already been called
  777. '               for this file copy (of type either
  778. '               gstrKEY_SHAREDFILE or gstrKEY_PRIVATE --
  779. '               see CopySection for an example of how
  780. '               this works).  See NewAction() and related
  781. '               functions in LOGGING.BAS for comments on
  782. '               using the logging function.
  783. '               Either CommitAction() or AbortAction() will
  784. '               allows be called by this procedure, and
  785. '               should not be done by the caller.
  786. '
  787. ' Returns: True if copy was successful, False otherwise
  788. '
  789. ' POSTCONDITION: The current action will be either committed or
  790. '                aborted.
  791. '-----------------------------------------------------------
  792. '
  793. Private Function CopyFile(ByVal strSrcDir As String, ByVal strDestDir As String, ByVal strSrcName As String, ByVal strDestName As String, ByVal fShared As Boolean, ByVal fSystem As Boolean) As Boolean
  794.     Const intUNKNOWN% = 0
  795.     Const intCOPIED% = 1
  796.     Const intNOCOPY% = 2
  797.     Const intFILEUPTODATE% = 3
  798.  
  799.     '
  800.     'VerInstallFile() Flags
  801.     '
  802.     Const VIFF_FORCEINSTALL% = &H1
  803.     Const VIF_TEMPFILE& = &H1
  804.     Const VIF_SRCOLD& = &H4
  805.     Const VIF_DIFFLANG& = &H8
  806.     Const VIF_DIFFCODEPG& = &H10
  807.     Const VIF_DIFFTYPE& = &H20
  808.     Const VIF_WRITEPROT& = &H40
  809.     Const VIF_FILEINUSE& = &H80
  810.     Const VIF_OUTOFSPACE& = &H100
  811.     Const VIF_ACCESSVIOLATION& = &H200
  812.     Const VIF_SHARINGVIOLATION = &H400
  813.     Const VIF_CANNOTCREATE = &H800
  814.     Const VIF_CANNOTDELETE = &H1000
  815.     Const VIF_CANNOTRENAME = &H2000
  816.     Const VIF_OUTOFMEMORY = &H8000&
  817.     Const VIF_CANNOTREADSRC = &H10000
  818.     Const VIF_CANNOTREADDST = &H20000
  819.     Const VIF_BUFFTOOSMALL = &H40000
  820.  
  821.     Static fIgnoreWarn As Boolean 'user warned about ignoring error?
  822.  
  823.     Dim strMsg As String
  824.     Dim lRC As Long
  825.     Dim lpTmpNameLen As Long
  826.     Dim intFlags As Integer
  827.     Dim intRESULT As Integer
  828.     Dim fFileAlreadyExisted
  829.     Dim strExt As String
  830.  
  831.     On Error Resume Next
  832.  
  833.     '
  834.     'Ensure that the source file is available for copying
  835.     '
  836.     If Not DetectFile(strSrcDir & strSrcName) Then
  837.         AbortAction
  838.         Exit Function
  839.     End If
  840.     
  841.     '
  842.     ' Make sure that the Destination path (including path, filename, commandline args, etc.
  843.     ' is not longer than the max allowed.
  844.     '
  845.     If Not fCheckFNLength(strDestDir & strDestName) Then
  846.         AbortAction
  847.         strMsg = ResolveResString(resCANTCOPYPATHTOOLONG) & vbLf & vbLf & ResolveResString(resCHOOSENEWDEST) & vbLf & vbLf & strDestDir & strDestName
  848.         MsgError strMsg, vbOKOnly, gstrSETMSG
  849.         ExitSetup frmCopy, gintRET_FATAL
  850.     End If
  851.     '
  852.     'Make the destination directory, prompt the user to retry if there is an error
  853.     '
  854.     If Not MakePath(strDestDir) Then
  855.         AbortAction ' Abort file copy
  856.         Exit Function
  857.     End If
  858.  
  859.     '
  860.     'Make sure we have the LFN (long filename) of the destination directory
  861.     '
  862.     strDestDir = GetLongPathName(strDestDir)
  863.     '
  864.     'GetLongPathName will string the final '\' off a path, so we need to restore
  865.     'it.
  866.     '
  867.     AddDirSep strDestDir
  868.     
  869.     '
  870.     'Setup for VerInstallFile call
  871.     '
  872.     lpTmpNameLen = gintMAX_SIZE
  873.     mstrVerTmpName = String$(lpTmpNameLen, 0)
  874.     intFlags = VIFF_FORCEINSTALL
  875.     fFileAlreadyExisted = FileExists(strDestDir & strDestName)
  876.  
  877.     intRESULT = intUNKNOWN
  878.  
  879.     Do While intRESULT = intUNKNOWN
  880.         'VerInstallFile under Windows 95 does not handle
  881.         '  long filenames, so we must give it the short versions.
  882.         Dim strShortSrcName As String
  883.         Dim strShortDestName As String
  884.         Dim strShortSrcDir As String
  885.         Dim strShortDestDir As String
  886.         Dim nFile As Integer
  887.  
  888.         strShortSrcName = strSrcName
  889.         strShortSrcDir = strSrcDir
  890.         strShortDestName = strDestName
  891.         strShortDestDir = strDestDir
  892.  
  893.         On Error GoTo UnexpectedErr
  894.         If Not IsWindowsNT() Then
  895.             'This conversion is not necessary under Windows NT
  896.             If Not FileExists(strDestDir & strDestName) Then
  897.                 'If the destination file does not already
  898.                 '  exist, we create a dummy with the correct
  899.                 '  (long) filename so that we can get its
  900.                 '  short filename for VerInstallFile.
  901.                 nFile = FreeFile
  902.                 Open strDestDir & strDestName For Output Access Write As #nFile
  903.                 Close #nFile
  904.             End If
  905.             SeparatePathAndFileName GetShortPathName(strSrcDir & strSrcName), strShortSrcDir, strShortSrcName
  906.             SeparatePathAndFileName GetShortPathName(strDestDir & strDestName), strShortDestDir, strShortDestName
  907.         End If
  908.         On Error Resume Next
  909.         lRC = VerInstallFile(intFlags, strShortSrcName, strShortDestName, strShortSrcDir, strShortDestDir, 0&, mstrVerTmpName, lpTmpNameLen)
  910.         If Err.Number <> 0 Then
  911.             '
  912.             'If the version DLL couldn't be found, then abort setup
  913.             '
  914.             ExitSetup frmCopy, gintRET_FATAL
  915.         End If
  916.  
  917.         If lRC = 0 Then
  918.             '
  919.             'File was successfully installed, increment reference count if needed
  920.             '
  921.             'One more kludge for long filenames: VerInstallFile may have renamed
  922.             'the file to its short version if it went through with the copy.
  923.             'Therefore we simply rename it back to what it should be.
  924.             Name strDestDir & strShortDestName As strDestDir & strDestName
  925.             intRESULT = intCOPIED
  926.         ElseIf lRC And VIF_SRCOLD Then
  927.             '
  928.             'Source file was older, so not copied, the existing version of the file
  929.             'will be used.  Increment reference count if needed
  930.             '
  931.             intRESULT = intFILEUPTODATE
  932.         ElseIf lRC And (VIF_DIFFLANG Or VIF_DIFFCODEPG Or VIF_DIFFTYPE) Then
  933.             '
  934.             'We retry and force installation for these cases.  You can modify the code
  935.             'here to prompt the user about what to do.
  936.             '
  937.             intFlags = VIFF_FORCEINSTALL
  938.         ElseIf lRC And VIF_WRITEPROT Then
  939.             strMsg = ResolveResString(resWRITEPROT)
  940.             CFMsg strMsg, strDestDir, strDestName, fIgnoreWarn, intRESULT
  941.         ElseIf lRC And VIF_FILEINUSE Then
  942.             strMsg = ResolveResString(resINUSE)
  943.             CFMsg strMsg, strDestDir, strDestName, fIgnoreWarn, intRESULT
  944.         ElseIf lRC And VIF_OUTOFSPACE Then
  945.             GetDrive strDestDir, strMsg
  946.             strMsg = ResolveResString(resOUTOFSPACE, gstrPIPE1, strMsg)
  947.             CFMsg strMsg, strDestDir, strDestName, fIgnoreWarn, intRESULT
  948.         ElseIf lRC And VIF_ACCESSVIOLATION Then
  949.             strMsg = ResolveResString(resACCESSVIOLATION)
  950.             CFMsg strMsg, strDestDir, strDestName, fIgnoreWarn, intRESULT
  951.         ElseIf lRC And VIF_SHARINGVIOLATION Then
  952.             strMsg = ResolveResString(resSHARINGVIOLATION)
  953.             CFMsg strMsg, strDestDir, strDestName, fIgnoreWarn, intRESULT
  954.         ElseIf lRC And VIF_OUTOFMEMORY Then
  955.             strMsg = ResolveResString(resOUTOFMEMORY)
  956.             CFMsg strMsg, strDestDir, strDestName, fIgnoreWarn, intRESULT
  957.         Else
  958.             '
  959.             ' For these cases, we generically report the error and do not install the file
  960.             ' unless this is an SMS install; in which case we abort.
  961.             '
  962.             If lRC And VIF_CANNOTCREATE Then
  963.                 strMsg = ResolveResString(resCANNOTCREATE)
  964.             ElseIf lRC And VIF_CANNOTDELETE Then
  965.                 strMsg = ResolveResString(resCANNOTDELETE)
  966.             ElseIf lRC And VIF_CANNOTRENAME Then
  967.                 strMsg = ResolveResString(resCANNOTRENAME)
  968.             ElseIf lRC And VIF_CANNOTREADSRC Then
  969.                 strMsg = ResolveResString(resCANNOTREADSRC)
  970.             ElseIf lRC And VIF_CANNOTREADDST Then
  971.                 strMsg = ResolveResString(resCANNOTREADDST)
  972.             ElseIf lRC And VIF_BUFFTOOSMALL Then
  973.                 strMsg = ResolveResString(resBUFFTOOSMALL)
  974.             End If
  975.  
  976.             strMsg = strMsg & ResolveResString(resNOINSTALL)
  977.             MsgError strMsg, vbOKOnly Or vbExclamation, gstrTitle
  978. #If SMS Then
  979.             If gfSMS Then
  980.                 ExitSetup frmSetup1, gintRET_FATAL
  981.             End If
  982. #End If
  983.             intRESULT = intNOCOPY
  984.         End If
  985.     Loop
  986.  
  987.     '
  988.     'If there was a temp file left over from VerInstallFile, remove it
  989.     '
  990.     If lRC And VIF_TEMPFILE Then
  991.         Kill mstrVerTmpName
  992.     End If
  993.  
  994.     'Get the UCase of the extension, for use below.
  995.     strExt = UCase$(Extension(strDestName))
  996.     'Abort or commit the current Action, and do reference counting
  997.     Select Case intRESULT
  998.         Case intNOCOPY
  999.             AbortAction
  1000.         Case intCOPIED
  1001.             DecideIncrementRefCount strDestDir & strDestName, fShared, fSystem, fFileAlreadyExisted
  1002.             Select Case strExt
  1003.                 Case gsEXT_FONTFON, gsEXT_FONTTTF 'strExt, gsEXT_FONTFON, and gsEXT_FONTTTF are uppercase
  1004.                     'do nothing
  1005.                 Case Else
  1006.                     AddActionNote ResolveResString(resLOG_FILECOPIED)
  1007.                     CommitAction
  1008.             End Select
  1009.             CopyFile = True
  1010.         Case intFILEUPTODATE
  1011.             'Note: This should never occur - we've already checked to see if the
  1012.             '   file was up to date before calling VerInstallFile. But we'll
  1013.             '   leave it here for completeness.
  1014.             DecideIncrementRefCount strDestDir & strDestName, fShared, fSystem, fFileAlreadyExisted
  1015.             Select Case strExt
  1016.                 Case gsEXT_FONTFON, gsEXT_FONTTTF 'strExt, gsEXT_FONTFON, and gsEXT_FONTTTF are uppercase
  1017.                     'do nothing
  1018.                 Case Else
  1019.                     AddActionNote ResolveResString(resLOG_FILEUPTODATE)
  1020.                     CommitAction
  1021.             End Select
  1022.             CopyFile = True
  1023.         Case Else
  1024.             AbortAction ' Defensive - this shouldn't be reached
  1025.     End Select
  1026.  
  1027.     Exit Function
  1028.  
  1029. UnexpectedErr:
  1030.     MsgError Err.Description & vbLf & vbLf & ResolveResString(resUNEXPECTED), vbOKOnly Or vbExclamation, gstrTitle
  1031.     ExitSetup frmCopy, gintRET_FATAL
  1032. End Function
  1033.  
  1034. '-----------------------------------------------------------
  1035. ' SUB: CFMsg
  1036. '
  1037. ' Displays a message related to a file copy issue.
  1038. '-----------------------------------------------------------
  1039. '
  1040. Private Sub CFMsg(strMsg As String, strDestDir As String, strDestName As String, fIgnoreWarn As Boolean, intRESULT As Integer)
  1041.     Const intNOCOPY% = 2
  1042.     Dim intMsgRet As Integer
  1043.  
  1044.     strMsg = strDestDir & strDestName & vbLf & vbLf & strMsg
  1045.     intMsgRet = MsgError(strMsg, vbAbortRetryIgnore Or vbExclamation Or vbDefaultButton2, gstrTitle)
  1046.     If gfNoUserInput Then intMsgRet = vbAbort
  1047.     Select Case intMsgRet
  1048.         Case vbAbort
  1049.             ExitSetup frmCopy, gintRET_ABORT
  1050.         Case vbIgnore
  1051.             If fIgnoreWarn Then
  1052.                 intRESULT = intNOCOPY
  1053.             Else
  1054.                 fIgnoreWarn = True
  1055.                 strMsg = strMsg & vbLf & vbLf & ResolveResString(resWARNIGNORE)
  1056.                 If MsgError(strMsg, vbYesNo Or vbQuestion Or vbDefaultButton2, gstrTitle) = vbYes Then
  1057.                     intRESULT = intNOCOPY
  1058.                 End If
  1059.             End If
  1060.     End Select
  1061. End Sub
  1062.  
  1063. '-----------------------------------------------------------
  1064. ' SUB: CopySection
  1065. '
  1066. ' Attempts to copy the files that need to be copied from
  1067. ' the named section of the setup info file (SETUP.LST)
  1068. '
  1069. ' IN: [strSection] - name of section to copy files from
  1070. '-----------------------------------------------------------
  1071. '
  1072. Public Sub CopySection(ByVal strSection As String)
  1073.     Dim strNewSrc As String
  1074.     Dim intIdx As Integer
  1075.     Dim sFile As FILEINFO
  1076.     Dim strLastFile As String
  1077.     Dim intRC As Integer
  1078.     Dim lThisFileSize As Long
  1079.     Dim strSrcDir As String
  1080.     Dim strDestDir As String
  1081.     Dim strSrcName As String
  1082.     Dim strDestName As String
  1083.     Dim strRegister As String
  1084.     Dim fFileWasUpToDate As Boolean
  1085.     Dim strMultDirBaseName As String
  1086.     Dim strMsg As String
  1087.     Dim strDetectPath As String
  1088.     Dim fOverWrite As Boolean
  1089.     Static fOverwriteAll As Boolean
  1090.     Dim strExt As String
  1091.     Dim sCurDate As String
  1092.     Dim sFileDate As String
  1093.     Dim lTime As FileTime
  1094.     Dim hFile As Long
  1095.     Dim frm As frmOverwrite
  1096.     Dim strDestVer As String
  1097.     Dim owValue As OverwriteReturnVal
  1098.     Dim fcValue As FileComparison
  1099.  
  1100.     On Error Resume Next
  1101.  
  1102.     strMultDirBaseName = ResolveResString(resCOMMON_MULTDIRBASENAME)
  1103.     intIdx = 1
  1104.  
  1105.     If Not FileExists(gsTEMPDIR) Then
  1106.         MkDir gsTEMPDIR
  1107.     End If
  1108.     '
  1109.     'For each file in the specified section, read info from the setup info file
  1110.     '
  1111.     Do While ReadSetupFileLine(strSection, intIdx, sFile)
  1112.         intRC = 0
  1113.  
  1114.         fFileWasUpToDate = False
  1115.  
  1116.         If UCase$(sFile.strSrcName) = UCase$(gstrAT & gstrFILE_MDAG) Then
  1117.             'We don't need to extract mdac_typ twice
  1118.             GoTo CSContinue
  1119.         End If
  1120.         If IsFileADXRedistFile(sFile.strSrcName) Then
  1121.             'We don't need to extract the DX Runtime more than once
  1122.             GoTo CSContinue
  1123.         End If
  1124.         
  1125.         strNewSrc = gsTEMPDIR & sFile.strDestName
  1126.         ExtractFileFromCab gsCABFULLNAME, sFile.strSrcName, strNewSrc, gintCabs, gstrSrcPath
  1127.         If FileExists(strNewSrc) Then
  1128.             sFile.strSrcName = gsTEMPDIR & sFile.strDestName
  1129.             sFile.intDiskNum = gintCurrentDisk
  1130.         End If
  1131.         '
  1132.         ' If a new disk is called for, or if for some reason we can't find the
  1133.         ' source path (user removed the install floppy, for instance) then
  1134.         ' prompt for the next disk.  The PromptForNextDisk function won't
  1135.         ' actually prompt the user unless it determines that the source drive
  1136.         ' contains removeable media or is a network connection.  Also, we don't
  1137.         ' prompt if this is a silent install.  It will fail later on a silent
  1138.         ' install when it can't find the file.
  1139.         '
  1140.         If (Not gfNoUserInput) And (sFile.intDiskNum <> gintCurrentDisk Or Not DirExists(gstrSrcPath)) Then
  1141.             PromptForNextDisk sFile.intDiskNum, sFile.strSrcName
  1142.         End If
  1143.  
  1144.         strSrcName = sFile.strSrcName
  1145.         '
  1146.         ' The file could exist in either the main source directory or
  1147.         ' in a subdirectory named DISK1, DISK2, etc.  Set the appropriate
  1148.         ' path.  If it's in neither place, it is an error and will be
  1149.         ' handled later.
  1150.         '
  1151.         If FileExists(strSrcName) Then
  1152.             strSrcDir = gsTEMPDIR
  1153.         Else
  1154.             '
  1155.             ' Can't find the file.
  1156.             '
  1157.             strDetectPath = gstrSrcPath & strMultDirBaseName & CStr(sFile.intDiskNum)
  1158.             If Not DirExists(strDetectPath) Then
  1159.                 strDetectPath = gstrSrcPath
  1160.             End If
  1161.             strMsg = ResolveResString(resCOMMON_CANTFINDSRCFILE, gstrPIPE1, strDetectPath & gstrSEP_DIR & strSrcName)
  1162.             MsgError strMsg, vbExclamation Or vbOKOnly, gstrTitle
  1163.             ExitSetup frmCopy, gintRET_FATAL
  1164.         End If
  1165.  
  1166.         strDestDir = sFile.strDestDir
  1167.         strDestName = sFile.strDestName
  1168.         
  1169.         'We need to go ahead and create the destination directory, or else
  1170.         'GetLongPathName() may fail
  1171.         If Not MakePath(strDestDir) Then
  1172.             intRC = vbIgnore
  1173.         End If
  1174.  
  1175.         If intRC <> vbIgnore Then
  1176.             strDestDir = GetLongPathName(strDestDir)
  1177.             '
  1178.             'GetLongPathName will string the final '\' off a path, so we need to
  1179.             'restore it.
  1180.             '
  1181.             AddDirSep strDestDir
  1182.  
  1183.             frmCopy.lblDestFile.Caption = strDestDir & sFile.strDestName
  1184.             frmCopy.lblDestFile.Refresh
  1185.  
  1186.             'Cache the UCase of the extension, for use below
  1187.             strExt = UCase$(Extension(sFile.strDestName))
  1188.             If UCase$(strDestName) = UCase$(gstrFILE_MDAG) Then
  1189.                 '
  1190.                 ' mdac_typ.EXE is installed temporarily.  We'll be
  1191.                 ' deleting it at the end of setup.  Set mdag = True
  1192.                 ' so we know we need to delete it later.
  1193.                 '
  1194.                 NewAction gstrKEY_TEMPFILE, gstrQUOTE & strDestDir & strDestName & gstrQUOTE
  1195.                 gfMDag = True
  1196.                 gstrMDagInstallPath = strDestDir & strDestName
  1197.             ElseIf strExt = gsEXT_FONTTTF Then 'Both strExt and gsEXT_FONTTTF are uppercase
  1198.                 'No new actions for fonts
  1199.             ElseIf strExt = gsEXT_FONTFON Then 'Both strExt and gsEXT_FONTFON are uppercase
  1200.                 'No new actions for fonts
  1201.             ElseIf sFile.fShared Then
  1202.                 NewAction gstrKEY_SHAREDFILE, gstrQUOTE & strDestDir & strDestName & gstrQUOTE
  1203.             ElseIf sFile.fSystem Then
  1204.                 NewAction gstrKEY_SYSTEMFILE, gstrQUOTE & strDestDir & strDestName & gstrQUOTE
  1205.             ElseIf strExt = UCase$(gsEXT_REG) Then 'strExt is uppercase; gsEXT_REG is not
  1206.                 If UCase$(Extension(sFile.strRegister)) = UCase$(gsEXT_REG) Then
  1207.                     'No new actions for registration files.
  1208.                 Else
  1209.                     NewAction gstrKEY_PRIVATEFILE, gstrQUOTE & strDestDir & strDestName & gstrQUOTE
  1210.                 End If
  1211.             Else
  1212.                 NewAction gstrKEY_PRIVATEFILE, gstrQUOTE & strDestDir & strDestName & gstrQUOTE
  1213.             End If
  1214.         End If
  1215.         
  1216.         '
  1217.         'If the file info just read from SETUP.LST is the application .EXE
  1218.         '(i.e.; it's the value of the AppExe Key in the [Setup] section,
  1219.         'then save it's full pathname for later use
  1220.         '
  1221.         If UCase$(strDestName) = UCase$(gstrAppExe) Then
  1222.             '
  1223.             'Used for creating a program manager icon in Form_Load of SETUP1.FRM
  1224.             'and for registering the per-app path
  1225.             '
  1226.             gsDest.strAppDir = strDestDir
  1227.         End If
  1228.  
  1229.         'Special case for RICHED32.DLL
  1230.         '-- we only install this file under Windows 95, not under Windows NT
  1231.         If UCase$(strDestName) = mstrFILE_RICHED32 Then 'mstrFILE_RICHED32 is uppercase
  1232.             If Not IsWindows95() Then
  1233.                 'We're not running under Win95 - do not install this file.
  1234.                 intRC = vbIgnore
  1235.                 LogNote ResolveResString(resCOMMON_RICHED32NOTCOPIED, gstrPIPE1, strDestName)
  1236.                 AbortAction
  1237.             End If
  1238.         End If
  1239.  
  1240.         strRegister = sFile.strRegister
  1241.  
  1242.         lThisFileSize = CalcFinalSize(sFile.lFileSize, sFile.strDestDir)
  1243.         '
  1244.         'Next we'll deal with whether this file needs updrading.
  1245.         '
  1246.         If intRC <> vbIgnore Then
  1247.             'Assume the file does need upgrading.
  1248.             fOverWrite = True
  1249.             'Unless the user has already decided to upgrade all files, check and
  1250.             'see if this file needs upgrading.
  1251.             owValue = owNo
  1252.             If fOverwriteAll Then
  1253.                 fcValue = SourceFileIsNewer(sFile, strSrcName, strSrcDir, strDestName, strDestDir, strDestVer)
  1254.                 If sFile.fDestDirRecognizedBySetupExe And FileInUse(strDestDir & strDestName) Then
  1255.                     If fcValue = fcOlder Then
  1256.                         '
  1257.                         'If the destination file is in use and it's a file
  1258.                         'that Setup.Exe recognizes, then there is no chance
  1259.                         'of succeeding a file upgrade, so just assume a Yes
  1260.                         'return from the dialog.
  1261.                         '
  1262.                         owValue = owYes
  1263.                     'Else
  1264.                     '    This will lead to an error when the file is copied, but
  1265.                     '    the error is legitimate.
  1266.                     End If
  1267.                 ElseIf fcValue = fcEquivalent Then
  1268.                     owValue = owYes
  1269.                 End If
  1270.             Else
  1271.                 Select Case SourceFileIsNewer(sFile, strSrcName, strSrcDir, strDestName, strDestDir, strDestVer)
  1272.                     Case fcEquivalent
  1273.                         owValue = owYes
  1274.                     Case fcOlder
  1275.                         If sFile.fDestDirRecognizedBySetupExe And FileInUse(strDestDir & strDestName) Then
  1276.                             '
  1277.                             'See previous comment.
  1278.                             '
  1279.                             owValue = owYes
  1280.                         Else
  1281.                             '
  1282.                             'Source file is not newer than destination file;
  1283.                             'prompt user for what to do
  1284.                             '
  1285.                             Set frm = New frmOverwrite
  1286.                             frm.FileName = strDestDir & strDestName
  1287.                             frm.Version = strDestVer
  1288.                             frm.Description = GetFileDescription(strDestDir & strDestName)
  1289.                             frm.Show vbModal, frmSetup1
  1290.                             owValue = frm.ReturnVal
  1291.                             Set frm = Nothing
  1292.                         End If
  1293.                 End Select
  1294.             End If
  1295.             Select Case owValue
  1296.                 Case owYes 'Keep this file; don't upgrade
  1297.                     fOverWrite = False
  1298.                     'We won't be copying the file below, so finish dealing
  1299.                     '   with it now.
  1300.                     intRC = vbIgnore
  1301.                     fFileWasUpToDate = True
  1302.                     If strExt = gsEXT_FONTTTF Then 'Both strExt and gsEXT_FONTTTF are uppercase
  1303.                         'Do nothing - fonts are not logged nor
  1304.                         '   refcounted.
  1305.                     ElseIf strExt = gsEXT_FONTFON Then 'Both strExt and gsEXT_FONTFON are uppercase
  1306.                         'Do nothing - fonts are not logged nor
  1307.                         '   refcounted.
  1308.                     Else
  1309.                         DecideIncrementRefCount strDestDir & strDestName, sFile.fShared, sFile.fSystem, True
  1310.                         AddActionNote ResolveResString(resLOG_FILEUPTODATE)
  1311.                         CommitAction
  1312.                     End If
  1313.                 Case owNoToAll 'Overwrite all files
  1314.                     fOverwriteAll = True
  1315.                 'Case owNo 'Overwrite the file
  1316.                     'We assumed this above, so we don't need to do
  1317.                     '   anything now.
  1318.                     'fOverWrite = True
  1319.             End Select
  1320.         End If
  1321.         '
  1322.         'After all of this, if we're still ready to copy, then give it a whirl!
  1323.         '
  1324.         If intRC <> vbIgnore Then
  1325.             ' CopyFile will increment the reference count for us, and will either
  1326.             ' commit or abort the current Action.
  1327.             'Turn off READONLY flag in case we copy.
  1328.             SetAttr strDestDir & strDestName, vbNormal
  1329.             If UCase$(Extension(sFile.strRegister)) <> UCase$(gsEXT_REG) Then
  1330.                 If CopyFile(strSrcDir, strDestDir, strDestName, strDestName, sFile.fShared, sFile.fSystem) Then
  1331.                     intRC = 0
  1332.                 Else
  1333.                     intRC = vbIgnore
  1334.                 End If
  1335.             End If
  1336.         End If
  1337.         '
  1338.         'Register fonts
  1339.         '
  1340.         Select Case strExt
  1341.         Case gsEXT_FONTTTF, gsEXT_FONTFON 'strExt, gsEXT_FONTTTF, and gsEXT_FONTFON are all uppercase
  1342.             AddFontResource strDestDir & strDestName
  1343.         End Select
  1344.         '
  1345.         'Save the paths of certain files for later use, if they were
  1346.         'successfully installed or were already on the system
  1347.         '
  1348.         If (intRC = 0 Or fFileWasUpToDate) Then
  1349.             Select Case UCase$(strDestName)
  1350.                 Case mstrFILE_AUTMGR32 'This is uppercase
  1351.                     '
  1352.                     'Used for creating an icon if installed
  1353.                     '
  1354.                     gsDest.strAUTMGR32 = strDestDir & mstrFILE_AUTMGR32
  1355.                 Case mstrFILE_RACMGR32 'This is uppercase
  1356.                     '
  1357.                     'Used for creating an icon if installed
  1358.                     '
  1359.                     gsDest.strRACMGR32 = strDestDir & mstrFILE_RACMGR32
  1360.             End Select
  1361.             '
  1362.             'If we successfully copied the file, and if registration information was
  1363.             'specified in the setup info file, save the registration info into an
  1364.             'array so that we can register all files requiring it in one fell swoop
  1365.             'after all the files have been copied.
  1366.             '
  1367.             If Len(strRegister) > 0 Then
  1368.                 Err.Clear
  1369.                 ReDim Preserve msRegInfo(UBound(msRegInfo) + 1)
  1370.  
  1371.                 If Err.Number <> 0 Then
  1372.                     ReDim msRegInfo(0)
  1373.                 End If
  1374.  
  1375.                 msRegInfo(UBound(msRegInfo)).strFilename = strDestDir & strDestName
  1376.  
  1377.                 Select Case UCase$(strRegister)
  1378.                     Case mstrDLLSELFREGISTER, mstrEXESELFREGISTER, mstrTLBREGISTER, mstrVBLREGISTER 'These are all uppercase
  1379.                         'Nothing in particular to do
  1380.                     Case mstrREMOTEREGISTER 'This is uppercase
  1381.                         'We need to look for and parse the corresponding "RemoteX=..." line
  1382.                         If Not ReadSetupRemoteLine(strSection, intIdx, msRegInfo(UBound(msRegInfo))) Then
  1383.                             MsgError ResolveResString(resREMOTELINENOTFOUND, gstrPIPE1, strDestName, gstrPIPE2, gstrINI_REMOTE & CStr(intIdx)), vbExclamation Or vbOKOnly, gstrTitle
  1384.                             ExitSetup frmSetup1, gintRET_FATAL
  1385.                         End If
  1386.                     Case Else
  1387.                         '
  1388.                         'If the registration info specified the name of a file with
  1389.                         'registration info (which we assume if a registration macro
  1390.                         'was not specified), then we also assume that, if no path
  1391.                         'information is available, this reginfo file is in the same
  1392.                         'directory as the file it registers
  1393.                         '
  1394.                         strRegister = ResolveDestDirs(strRegister)
  1395.                         If InStr(strRegister, gstrSEP_DIR) = 0 Then 'This search is case-sensitive, as it should be
  1396.                             strRegister = strSrcDir & strRegister
  1397.                         End If
  1398.                 End Select
  1399.  
  1400.                 If UCase$(Extension(strRegister)) = UCase$(gsEXT_REG) Then
  1401.                     SyncShell gsREGEDIT & strQuoteString(strRegister), INFINITE
  1402.                 End If
  1403.                 msRegInfo(UBound(msRegInfo)).strRegister = strRegister
  1404.             End If
  1405.         End If
  1406.  
  1407.         strLastFile = sFile.strDestName
  1408.  
  1409. CSContinue:
  1410.         '
  1411.         'Update the copy status bar.  We need to do the update regardless of whether a
  1412.         'file was actually copied or not.
  1413.         '
  1414.         glTotalCopied = glTotalCopied + lThisFileSize
  1415.         UpdateStatus frmCopy.picStatus, glTotalCopied / mlTotalToCopy
  1416.  
  1417.         sFileDate = Format$(FileDateTime(sFile.strDestDir & sFile.strDestName), "m/d/yyyy h:m")
  1418.         sCurDate = Format$(Now, "m/d/yyyy h:m")
  1419.  
  1420.         If sFileDate = sCurDate Then
  1421.             lTime = GetFileTime(sFile.varDate)
  1422.             hFile = CreateFile(sFile.strDestDir & sFile.strDestName, GENERIC_WRITE Or GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
  1423.             SetFileTime hFile, lTime, lTime, lTime
  1424.             DoEvents
  1425.             CloseHandle hFile
  1426.         Else
  1427.             '
  1428.             'Give a chance for the 'Cancel' button command to be processed if it was pressed
  1429.             '
  1430.             DoEvents
  1431.         End If
  1432.         'Delete the files after copy...
  1433.         SetAttr gsTEMPDIR & sFile.strDestName, vbNormal
  1434.         Kill gsTEMPDIR & sFile.strDestName
  1435.         intIdx = intIdx + 1
  1436.     Loop
  1437.  
  1438.     Err.Clear
  1439. End Sub
  1440.  
  1441. '-----------------------------------------------------------
  1442. ' FUNCTION: SourceFileIsNewer
  1443. '
  1444. ' Determines whether a file to be installed is newer than an
  1445. ' existing file already on the system.
  1446. '
  1447. ' IN: [sFile] - structure containing information about the source file
  1448. '     [strSrcName] - name of source file
  1449. '     [strSrcDir] - location of source file
  1450. '     [strDestName] - name of destination file
  1451. '     [strDestDir] - destination directory for file
  1452. ' OUT: [strDestVer] - a string representing the version of the destination file
  1453. '
  1454. ' Returns: True if there is no existing (destination) file.
  1455. '          True if the destination file does exist and the
  1456. '          source file has a newer version.
  1457. '          True if the destination file does exist but one
  1458. '          or both files does not have version information
  1459. '          and the source file has a newer timestamp.
  1460. '          False otherwise
  1461. '-----------------------------------------------------------
  1462. '
  1463. Private Function SourceFileIsNewer(sFile As FILEINFO, strSrcName As String, strSrcDir As String, strDestName As String, strDestDir As String, ByRef strDestVer As String) As FileComparison
  1464.     Dim fSrcVer As Boolean
  1465.     Dim sSrcVerInfo As VERINFO
  1466.     Dim fRemoteReg As Boolean
  1467.     Dim sDestVerInfo As VERINFO
  1468.     Dim datDest As Date
  1469.     '
  1470.     'The stuff below tries to save some time by pre-checking whether a file
  1471.     'should be installed before VerInstallFile does its thing.
  1472.     'Basically, if both files have version numbers, they are compared.
  1473.     'Otherwise, we compare date.
  1474.     '
  1475.     On Error Resume Next
  1476.     strDestVer = vbNullString
  1477.     '
  1478.     'Always attempt to get the source file version number.  If the setup
  1479.     'info file did not contain a version number (sSrcVerInfo.nMSHi =
  1480.     'gintNOVERINFO), we attempt to read the version number from the source
  1481.     'file.
  1482.     '
  1483.     fSrcVer = True
  1484.     sSrcVerInfo = sFile.sVerInfo
  1485.     If sSrcVerInfo.FileVerPart1 = gintNOVERINFO Then
  1486.         fSrcVer = GetFileVerStruct(strSrcDir & strSrcName, sSrcVerInfo)
  1487.     End If
  1488.     '
  1489.     'If there is an existing destination file with version information, then
  1490.     'compare its version number to the source file version number.
  1491.     '
  1492.     If fSrcVer Then
  1493.         fRemoteReg = (UCase$(sFile.strRegister) = mstrREMOTEREGISTER) 'mstrREMOTEREGISTER is uppercase
  1494.         If GetFileVerStruct(strDestDir & strDestName, sDestVerInfo, fRemoteReg) Then
  1495.             With sDestVerInfo
  1496.                 strDestVer = CStr(.FileVerPart1) & "." & _
  1497.                              CStr(.FileVerPart2) & "." & _
  1498.                              CStr(.FileVerPart3) & "." & _
  1499.                              CStr(.FileVerPart4)
  1500.             End With
  1501.             'Both source and destinations have versions. Compare them.
  1502.             SourceFileIsNewer = IsNewerVer(sSrcVerInfo, sDestVerInfo)
  1503.             Err.Clear
  1504.             Exit Function
  1505.         End If
  1506.     End If
  1507.     '
  1508.     'Since neither file has a version, the best we can do is compare dates.
  1509.     '
  1510.     Err.Clear
  1511.     datDest = FileDateTime(strDestDir & strDestName)
  1512.     If Err.Number = 0 Then
  1513.         If sFile.varDate < datDest Then
  1514.             SourceFileIsNewer = fcNewer
  1515.         ElseIf sFile.varDate = datDest Then
  1516.             SourceFileIsNewer = fcEquivalent
  1517.         Else
  1518.             SourceFileIsNewer = fcOlder
  1519.         End If
  1520.     Else
  1521.         'Evidently the destination file does not exist. Therefore the source
  1522.         '   file should be copied and can be considered newer.
  1523.         SourceFileIsNewer = fcNewer
  1524.     End If
  1525.     Err.Clear
  1526. End Function
  1527.  
  1528. '-----------------------------------------------------------
  1529. ' SUB: fCreateShellGroup
  1530. '
  1531. ' Creates a new program group off of Start>Programs in the
  1532. ' Windows 95 shell if the specified folder doesn't already exist.
  1533. '
  1534. ' IN: [strFolderName] - text name of the folder.
  1535. '                      This parameter may not contain
  1536. '                      backslashes.
  1537. '                      ex: "My Application" - this creates
  1538. '                        the folder Start>Programs>My Application
  1539. '     [fRetOnerr] - Whether or not this routine should return if
  1540. '                   there is an error creating the group.  If false,
  1541. '                   setup aborts and does not return.  Set this to
  1542. '                   true if the user can do something to correct the
  1543. '                   error.  E.g., they entered a group name in the
  1544. '                   Choose Program Group dialog as opposed to calling
  1545. '                   this routine when creating the Remote Automation
  1546. '                   group in which the user had no control.
  1547. '     [fLog] - Whether or not to write to the logfile (default
  1548. '                is true if missing)
  1549. '-----------------------------------------------------------
  1550. '
  1551. Public Function fCreateShellGroup(ByVal strFolderName As String, fRetOnErr As Boolean, Optional ByVal fLog As Boolean = True, Optional ByVal fPriv As Boolean = True, Optional ByVal fStartMenu As Boolean = False) As Boolean
  1552.     Dim oMalloc As IVBMalloc
  1553.     Dim fSuccess As Boolean
  1554.     Dim sPath As String
  1555.     Dim IDL As Long
  1556.     Dim lPrograms As SpecialFolderIDs
  1557.  
  1558.     ReplaceDoubleQuotes strFolderName
  1559.     
  1560.     If Len(strFolderName) = 0 Then
  1561.         Exit Function
  1562.     End If
  1563.  
  1564. Retry:
  1565.     If IsWindows95() Then
  1566.         fPriv = True
  1567.     End If
  1568.     If fPriv Then
  1569.         If fStartMenu Then
  1570.             lPrograms = sfidSTARTMENU
  1571.         Else
  1572.             lPrograms = sfidPROGRAMS
  1573.         End If
  1574.     Else
  1575.         If fStartMenu Then
  1576.             lPrograms = sfidCOMMON_STARTMENU
  1577.         Else
  1578.             lPrograms = sfidCOMMON_PROGRAMS
  1579.         End If
  1580.     End If
  1581.     ' Fill the item id list with the pointer of each folder item, rtns 0 on success
  1582.     If SHGetSpecialFolderLocation(frmSetup1.hWnd, lPrograms, IDL) = NOERROR Then
  1583.         sPath = String$(gintMAX_PATH_LEN, 0)
  1584.         SHGetPathFromIDListA IDL, sPath
  1585.         SHGetMalloc oMalloc
  1586.         oMalloc.Free IDL
  1587.         sPath = StringFromBuffer(sPath)
  1588.     End If
  1589.     AddDirSep sPath
  1590.     sPath = sPath & strFolderName
  1591.     fSuccess = MakePath(sPath)
  1592.     If Not fSuccess Then
  1593.         If gfNoUserInput Or (MsgError(ResolveResString(resCANTCREATEPROGRAMGROUP, gstrPIPE1, strFolderName), vbRetryCancel Or vbExclamation, gstrTitle)) = vbCancel Then
  1594.             ExitSetup frmSetup1, gintRET_EXIT
  1595.             GoTo Retry
  1596.         End If
  1597.         '
  1598.         ' Determine if we should return so the user can
  1599.         ' correct the situation.
  1600.         '
  1601.         If Not fRetOnErr Then
  1602.             '
  1603.             ' Return so we can exit setup.
  1604.             '
  1605.             GoTo Retry
  1606.         End If
  1607.     End If
  1608.  
  1609.     fCreateShellGroup = fSuccess
  1610. End Function
  1611.  
  1612. '-----------------------------------------------------------
  1613. ' SUB: CreateShellLink
  1614. '
  1615. ' Creates (or replaces) a link in either Start>Programs or
  1616. ' any of its immediate subfolders in the Windows 95 shell.
  1617. '
  1618. ' IN: [strLinkPath] - full path to the target of the link
  1619. '                     Ex: 'c:\Program Files\My Application\MyApp.exe"
  1620. '     [strLinkArguments] - command-line arguments for the link
  1621. '                     Ex: '-f -c "c:\Program Files\My Application\MyApp.dat" -q'
  1622. '     [strLinkName] - text caption for the link
  1623. '     [fLog] - Whether or not to write to the logfile (default
  1624. '                is true if missing)
  1625. '
  1626. ' OUT:
  1627. '   The link will be created in the folder strGroupName
  1628. '-----------------------------------------------------------
  1629. '
  1630. Public Sub CreateShellLink(ByVal strLinkPath As String, ByVal strGroupName As String, ByVal strLinkArguments As String, ByVal strLinkName As String, ByVal fPrivate As Boolean, sParent As String, Optional ByVal fLog As Boolean = True)
  1631.     Dim fSuccess As Boolean
  1632.     Dim intMsgRet As Integer
  1633.  
  1634.     If fLog Then
  1635.         NewAction gstrKEY_SHELLLINK, gstrQUOTE & strUnQuoteString(strGroupName) & gstrQUOTE & ", " & gstrQUOTE & strUnQuoteString(strLinkName) & gstrQUOTE
  1636.     End If
  1637.  
  1638.     strLinkName = strUnQuoteString(strLinkName)
  1639.     strLinkPath = strUnQuoteString(strLinkPath)
  1640.     
  1641.     If StrPtr(strLinkArguments) = 0 Then strLinkArguments = ""
  1642.  
  1643. Retry:
  1644.     fSuccess = OSfCreateShellLink(strGroupName, strLinkName, strLinkPath, strLinkArguments, fPrivate, sParent)  'the path should never be enclosed in double quotes
  1645.     If fSuccess Then
  1646.         If fLog Then
  1647.             CommitAction
  1648.         End If
  1649.     Else
  1650.         intMsgRet = MsgError(ResolveResString(resCANTCREATEPROGRAMICON, gstrPIPE1, strLinkName), vbAbortRetryIgnore Or vbExclamation, gstrTitle)
  1651.         If gfNoUserInput Then
  1652.             intMsgRet = vbAbort
  1653.         End If
  1654.         Select Case intMsgRet
  1655.             Case vbAbort
  1656.                 ExitSetup frmSetup1, gintRET_ABORT
  1657.                 GoTo Retry
  1658.             Case vbRetry
  1659.                 GoTo Retry
  1660.             Case vbIgnore
  1661.                 If fLog Then
  1662.                     AbortAction
  1663.                 End If
  1664.         End Select
  1665.     End If
  1666. End Sub
  1667.  
  1668. '-----------------------------------------------------------
  1669. ' FUNCTION: DecideIncrementRefCount
  1670. '
  1671. ' Increments the reference count of a file under 32-bits
  1672. ' if the file is a shared file.
  1673. '
  1674. ' IN: [strFullPath] - full pathname of the file to reference
  1675. '                     count.  Example:
  1676. '                     'C:\MYAPP\MYAPP.DAT'
  1677. '     [fShared] - whether the file is shared or private
  1678. '     [fSystem] - The file is a system file
  1679. '     [fFileAlreadyExisted] - whether or not the file already
  1680. '                             existed on the hard drive
  1681. '                             before our setup program
  1682. '-----------------------------------------------------------
  1683. '
  1684. Private Sub DecideIncrementRefCount(ByVal strFullPath As String, ByVal fShared As Boolean, ByVal fSystem As Boolean, ByVal fFileAlreadyExisted As Boolean)
  1685.     'Reference counting takes place under both Windows 95 and Windows NT
  1686.     If fShared Or fSystem Then
  1687.         IncrementRefCount strFullPath, fFileAlreadyExisted
  1688.     End If
  1689. End Sub
  1690.             
  1691. '-----------------------------------------------------------
  1692. ' FUNCTION: DetectFile
  1693. '
  1694. ' Detects whether the specified file exists.  If it can't
  1695. ' be found, the user is given the opportunity to abort,
  1696. ' retry, or ignore finding the file.  This call is used,
  1697. ' for example, to ensure that a floppy with the specified
  1698. ' file name is in the drive before continuing.
  1699. '
  1700. ' IN: [strFileName] - name of file to detect, usually
  1701. '                     should include full path, Example:
  1702. '                     'A:\MYAPP.DAT'
  1703. '
  1704. ' Returns: TRUE if the file was detected, False if
  1705. '          the user chose ignore when the file couldn't
  1706. '          be found, or calls ExitSetup upon 'Abort'
  1707. '-----------------------------------------------------------
  1708. '
  1709. Private Function DetectFile(ByVal strFilename As String) As Boolean
  1710.     Dim strMsg As String
  1711.     Dim iRet As Integer
  1712.  
  1713.     DetectFile = True
  1714.  
  1715.     Do Until FileExists(strFilename)
  1716.         strMsg = ResolveResString(resCANTOPEN) & vbLf & vbLf & strFilename
  1717.         iRet = MsgError(strMsg, vbAbortRetryIgnore Or vbExclamation Or vbDefaultButton2, gstrSETMSG)
  1718.         If gfNoUserInput Then iRet = vbAbort
  1719.         Select Case iRet
  1720.             Case vbAbort
  1721.                 ExitSetup frmCopy, gintRET_ABORT
  1722.             Case vbIgnore
  1723.                 DetectFile = False
  1724.                 Exit Do
  1725.         End Select
  1726.     Loop
  1727. End Function
  1728.  
  1729. '-----------------------------------------------------------
  1730. ' SUB: EtchedLine
  1731. '
  1732. ' Draws an 'etched' line upon the specified form starting
  1733. ' at the X,Y location passed in and of the specified length.
  1734. ' Coordinates are in the current ScaleMode of the passed
  1735. ' in form.
  1736. '
  1737. ' IN: [frmEtch] - form to draw the line upon
  1738. '     [intX1] - starting horizontal of line
  1739. '     [intY1] - starting vertical of line
  1740. '     [intLength] - length of the line
  1741. '-----------------------------------------------------------
  1742. '
  1743. Public Sub EtchedLine(frmEtch As Form, ByVal intX1 As Integer, ByVal intY1 As Integer, ByVal intLength As Integer)
  1744.     Const lWHITE& = vb3DHighlight
  1745.     Const lGRAY& = vb3DShadow
  1746.  
  1747.     Dim sngPixelX As Single
  1748.     Dim sngPixelY As Single
  1749.  
  1750.     sngPixelX = frmEtch.ScaleX(1, vbPixels, frmEtch.ScaleMode)
  1751.     sngPixelY = frmEtch.ScaleY(1, vbPixels, frmEtch.ScaleMode)
  1752.  
  1753.     frmEtch.Line (intX1, intY1)-(intX1 + intLength + sngPixelX, intY1 + sngPixelY), lWHITE, BF
  1754.     frmEtch.Line (intX1, intY1)-(intX1 + intLength, intY1), lGRAY
  1755. End Sub
  1756.  
  1757. '-----------------------------------------------------------
  1758. ' SUB: ExeSelfRegister
  1759. '
  1760. ' Synchronously runs the file passed in (which should be
  1761. ' an executable file that supports the /REGSERVER switch,
  1762. ' for instance, a VB-generated ActiveX Component .EXE).
  1763. '
  1764. ' IN: [strFileName] - .EXE file to register
  1765. '-----------------------------------------------------------
  1766. '
  1767. Private Sub ExeSelfRegister(ByVal strFilename As String)
  1768.     Const strREGSWITCH$ = " /REGSERVER"
  1769.     '
  1770.     'Synchronously shell out and run the .EXE with the self registration switch
  1771.     '
  1772.     SyncShell AddQuotesToFN(strFilename) & strREGSWITCH, INFINITE, , True
  1773. End Sub
  1774.  
  1775. '-----------------------------------------------------------
  1776. ' SUB: ExitSetup
  1777. '
  1778. ' Handles shutdown of the setup app.  Depending upon the
  1779. ' value of the intExitCode parm, may prompt the user and
  1780. ' exit the sub if the user chooses to cancel the exit
  1781. ' process.
  1782. '
  1783. ' IN: [frm] - active form to unload upon exit
  1784. '     [intExitCode] - code specifying exit action
  1785. '-----------------------------------------------------------
  1786. '
  1787. Public Sub ExitSetup(frm As Form, intExitCode As Integer)
  1788.     Const sKEY As String = "Software\Microsoft\Windows\CurrentVersion\RunOnce\Setup"
  1789.     Const sValue As String = "Configuring Data Access"
  1790.  
  1791.     Const iSUCCESS = 0
  1792.     Const iFAIL = 1
  1793.     Dim strMsg As String
  1794.     Dim strSilent As String
  1795.     Dim fNeedReboot As Boolean
  1796.     Dim iRet As Integer
  1797.  
  1798.     Dim sRet As String
  1799.     Dim hKey As Long
  1800.  
  1801.     Dim nErrorLevel As Integer
  1802.  
  1803.     On Error Resume Next
  1804.     '
  1805.     ' If we aren't running in silent or sms mode give
  1806.     ' the user a chance to try again, if applicable.
  1807.     '
  1808.     If Not gfNoUserInput Then
  1809.         Select Case intExitCode
  1810.             Case gintRET_EXIT
  1811.                 '
  1812.                 'If user chose an Exit or Cancel button
  1813.                 '
  1814.                 If MsgWarning(ResolveResString(resASKEXIT), vbQuestion Or vbYesNo Or vbDefaultButton2, gstrTitle) = vbNo Then
  1815.                     Exit Sub
  1816.                 End If
  1817.             Case gintRET_ABORT
  1818.                 '
  1819.                 'If user chose to abort before a pending action
  1820.                 '
  1821.                 strMsg = ResolveResString(resINCOMPLETE) & vbLf & vbLf & ResolveResString(resQUITNOW) & vbLf & vbLf
  1822.                 strMsg = strMsg & ResolveResString(resQUITSETUP)
  1823.                 If MsgWarning(strMsg, vbQuestion Or vbYesNo Or vbDefaultButton2, gstrSETMSG) = vbNo Then
  1824.                     Exit Sub
  1825.                 End If
  1826.         End Select
  1827.     End If
  1828.  
  1829.     'Abort any pending actions
  1830.     Do While fWithinAction()
  1831.         AbortAction
  1832.     Loop
  1833.     '
  1834.     'Close all files
  1835.     '
  1836.     Close
  1837.     '
  1838.     'Clean up any temporary files from VerInstallFile
  1839.     '
  1840.     Kill mstrVerTmpName
  1841.  
  1842.     If frm.hWnd <> frmSetup1.hWnd Then
  1843.         Unload frm
  1844.     End If
  1845.     
  1846.     If frmSetup1.Visible Then frmSetup1.SetFocus
  1847.  
  1848.     '
  1849.     'Give appropriate ending message depending upon exit code
  1850.     '
  1851.     Select Case intExitCode
  1852.         Case gintRET_EXIT, gintRET_ABORT
  1853. #If SMS Then
  1854.             gfSMSStatus = False
  1855. #End If
  1856.             strMsg = ResolveResString(resINTERRUPTED, gstrPIPE1, gstrAppName) & vbLf & vbLf & ResolveResString(resCANRUN, gstrPIPE1, gstrAppName)
  1857.             MsgWarning strMsg, vbOKOnly Or vbCritical, gstrTitle
  1858.         Case gintRET_FATAL
  1859. #If SMS Then
  1860.             gfSMSStatus = False
  1861. #End If
  1862.             MsgError ResolveResString(resERROR, gstrPIPE1, gstrAppName), vbOKOnly Or vbCritical, gstrTitle
  1863.         Case gintRET_FINISHEDSUCCESS
  1864. #If SMS Then
  1865.             gfSMSStatus = True
  1866.             '
  1867.             ' Don't log this message to SMS since it is only a confirmation.
  1868.             '
  1869.             gfDontLogSMS = True
  1870. #End If
  1871.             MsgFunc ResolveResString(resSUCCESS, gstrPIPE1, gstrAppName), vbOKOnly, gstrTitle
  1872.         Case Else
  1873.             strMsg = ResolveResString(resINTERRUPTED, gstrPIPE1, gstrAppName) & vbLf & vbLf & ResolveResString(resCANRUN, gstrPIPE1, gstrAppName)
  1874.             MsgWarning strMsg, vbOKOnly Or vbCritical, gstrTitle
  1875.     End Select
  1876.  
  1877.     'Stop logging
  1878.     DisableLogging
  1879.  
  1880.     ' Clean up a successful installation
  1881.     If (intExitCode = gintRET_FINISHEDSUCCESS) Then
  1882.         'Check to see if we need to reboot for mdac_typ
  1883.         If RegOpenKey(HKEY_LOCAL_MACHINE, sKEY, hKey) Then
  1884.             If RegQueryStringValue(hKey, sValue, sRet) Then
  1885.                 'We need to reboot
  1886.                 'Warn the user before rebooting.  If they choose to reboot, do so, otherwise
  1887.                 'Warn them again.
  1888.                 If MsgBox(ResolveResString(resREBOOT), vbYesNo Or vbInformation, gstrTitle) = vbYes Then
  1889.                     fNeedReboot = True
  1890.                 Else
  1891.                     fNeedReboot = False
  1892.                     intExitCode = gintRET_FATAL
  1893.                     MsgBox ResolveResString(resREBOOTNO), vbOKOnly Or vbExclamation, gstrTitle
  1894.                 End If
  1895.             End If
  1896.         End If
  1897.         'Check to see if we need to reboot for DX Setup
  1898.         If gfDXReboot Then
  1899.             'We need to reboot
  1900.             'Warn the user before rebooting.  If they choose to reboot, do so, otherwise
  1901.             'Warn them again.
  1902.             If MsgBox(ResolveResString(resREBOOT), vbYesNo Or vbInformation, gstrTitle) = vbYes Then
  1903.                 fNeedReboot = True
  1904.             Else
  1905.                 fNeedReboot = False
  1906.                 intExitCode = gintRET_FATAL
  1907.                 MsgBox ResolveResString(resREBOOTNO), vbOKOnly Or vbExclamation, gstrTitle
  1908.             End If
  1909.         End If
  1910.     Else
  1911.         'Setup has been aborted for one reason or another
  1912.         If Len(gstrAppRemovalEXE) > 0 Then
  1913.             '
  1914.             ' We don't want to log this message to sms because it is
  1915.             ' only a confirmation message.
  1916.             '
  1917. #If SMS Then
  1918.             gfDontLogSMS = True
  1919. #End If
  1920.             MsgFunc ResolveResString(resLOG_ABOUTTOREMOVEAPP), vbInformation Or vbOKOnly, gstrTitle
  1921.             
  1922.             Err.Clear
  1923.             '
  1924.             ' Ready to run the installer.  Determine if this is a
  1925.             ' silent uninstall or not.
  1926.             '
  1927.             If gfSilent Then
  1928.                 strSilent = gstrSilentLog
  1929.             Else
  1930.                 strSilent = vbNullString
  1931.             End If
  1932.  
  1933.             Select Case intExitCode
  1934.                 Case gintRET_FATAL
  1935.                     nErrorLevel = APPREMERR_FATAL
  1936.                 Case gintRET_EXIT
  1937.                     nErrorLevel = APPREMERR_USERCANCEL
  1938.                 Case gintRET_ABORT
  1939.                     nErrorLevel = APPREMERR_NONFATAL
  1940.                 Case Else
  1941.                     nErrorLevel = APPREMERR_FATAL
  1942.             End Select
  1943.  
  1944. #If SMS Then
  1945.             Shell GetAppRemovalCmdLine(gstrAppRemovalEXE, gstrAppRemovalLog, strSilent, gfSMS, nErrorLevel, True), vbNormalFocus
  1946. #Else
  1947.             Shell GetAppRemovalCmdLine(gstrAppRemovalEXE, gstrAppRemovalLog, strSilent, nErrorLevel, True), vbNormalFocus
  1948. #End If
  1949.             If Err.Number <> 0 Then
  1950.                 MsgError Err.Description & vbLf & vbLf & ResolveResString(resLOG_CANTRUNAPPREMOVER), vbExclamation Or vbOKOnly, gstrTitle
  1951.             End If
  1952.  
  1953.             'Since the app removal program will attempt to delete this program and all of our runtime
  1954.             'files, we should exit as soon as possible (otherwise the app remover will not be
  1955.             'able to remove these files)
  1956.         End If
  1957.         
  1958.         'Note: We do not delete the logfile if an error occurs.
  1959.         'The application removal EXE will do that if needed.
  1960.         
  1961.     End If
  1962.     
  1963.     Unload frmSetup1
  1964.  
  1965. #If SMS Then
  1966.     If gfSMS Then
  1967.         WriteMIF gstrMIFFile, gfSMSStatus, gstrSMSDescription
  1968.     End If
  1969. #End If
  1970.  
  1971.     'Try the reboot (if necessary)...
  1972.     If fNeedReboot Then RebootSystem
  1973.     'End the program
  1974.     If intExitCode = gintRET_FINISHEDSUCCESS Then
  1975.         ExitProcess iSUCCESS
  1976.     Else
  1977.         ExitProcess iFAIL
  1978.     End If
  1979. End Sub
  1980.  
  1981. '-----------------------------------------------------------
  1982. ' FUNCTION: ProcessCommandLine
  1983. '
  1984. ' Processes the command-line arguments
  1985. '
  1986. ' OUT: Fills in the passed-in byref parameters as appropriate
  1987. '-----------------------------------------------------------
  1988. '
  1989. #If SMS Then
  1990. Public Sub ProcessCommandLine(ByVal strCommand As String, ByRef fSilent As Boolean, ByRef strSilentLog As String, ByRef fSMS As Boolean, ByRef strMIFFile As String, ByRef strSrcPath As String, ByRef strAppRemovalLog As String, ByRef strAppRemovalEXE As String)
  1991. #Else
  1992. Public Sub ProcessCommandLine(ByVal strCommand As String, ByRef fSilent As Boolean, ByRef strSilentLog As String, ByRef strSrcPath As String, ByRef strAppRemovalLog As String, ByRef strAppRemovalEXE As String)
  1993. #End If
  1994.     Dim fErr As Boolean
  1995.     Dim intAnchor As Integer
  1996.     
  1997.     strSrcPath = vbNullString
  1998.     strAppRemovalLog = vbNullString
  1999.     
  2000.     strCommand = Trim$(strCommand)
  2001.     
  2002.     '
  2003.     ' First, check to see if this is supposed to be a silent
  2004.     ' install (/s on the command line followed by
  2005.     ' a log file name) and set global variables appropriately.
  2006.     '
  2007.     ' If you are designing a silent install, the /s
  2008.     ' command line parameter should be added to the setup.exe
  2009.     ' command.  It will automatically be passed to setup1 as the
  2010.     ' first parameter.
  2011.     '
  2012.     ' The filename that follows the /s parameter must
  2013.     ' include the full path name.
  2014.     '
  2015.     intAnchor = InStr(LCase$(strCommand), gstrSwitchPrefix2 & gstrSILENTSWITCH)
  2016.     If intAnchor > 0 Then
  2017.         fSilent = True
  2018.         strCommand = Trim$(Mid$(strCommand, intAnchor + 2))
  2019.         strSilentLog = strExtractFilenameArg(strCommand, fErr)
  2020.         If fErr Then GoTo BadCommandLine
  2021.     Else
  2022.         fSilent = False
  2023.     End If
  2024. #If SMS Then
  2025.     fSMS = False
  2026. #End If
  2027.  
  2028.     '
  2029.     ' We expect to find the source directory,
  2030.     ' name/path of the logfile, and name/path
  2031.     ' of the app removal executable, separated only by
  2032.     ' spaces
  2033.     '
  2034.     strSrcPath = strExtractFilenameArg(strCommand, fErr)
  2035.     If fErr Then GoTo BadCommandLine
  2036.  
  2037.     strAppRemovalLog = strExtractFilenameArg(strCommand, fErr)
  2038.     If fErr Then GoTo BadCommandLine
  2039.  
  2040.     strAppRemovalEXE = strExtractFilenameArg(strCommand, fErr)
  2041.     If fErr Then GoTo BadCommandLine
  2042.  
  2043.     ' Both the app removal logfile and executable must exist
  2044.     If Not FileExists(strAppRemovalLog) Then
  2045.         GoTo BadAppRemovalLog
  2046.     End If
  2047.  
  2048.     If Not FileExists(strAppRemovalEXE) Then
  2049.         GoTo BadAppRemovalEXE
  2050.     End If
  2051.  
  2052.     ' Last check:  There should be nothing else on the command line
  2053.     strCommand = Trim$(strCommand)
  2054.     If Len(strCommand) > 0 Then
  2055.         GoTo BadCommandLine
  2056.     End If
  2057.  
  2058. Exit Sub
  2059. BadAppRemovalLog:
  2060.     MsgError ResolveResString(resCANTFINDAPPREMOVALLOG, gstrPIPE1, strAppRemovalLog), vbExclamation Or vbOKOnly, gstrTitle
  2061.     ExitSetup frmSetup1, gintRET_FATAL
  2062.     
  2063. BadAppRemovalEXE:
  2064.     MsgError ResolveResString(resCANTFINDAPPREMOVALEXE, gstrPIPE1, strAppRemovalEXE), vbExclamation Or vbOKOnly, gstrTitle
  2065.     ExitSetup frmSetup1, gintRET_FATAL
  2066.     
  2067. BadCommandLine:
  2068.     MsgError ResolveResString(resBADCOMMANDLINE), vbExclamation Or vbOKOnly, gstrTitle
  2069.     ExitSetup frmSetup1, gintRET_FATAL
  2070. End Sub
  2071.  
  2072. '-----------------------------------------------------------
  2073. ' FUNCTION: GetDrivesAllocUnit
  2074. '
  2075. ' Gets the minimum file size allocation unit for the
  2076. ' specified drive
  2077. '
  2078. ' IN: [strDrive] - Drive to get allocation unit for
  2079. '
  2080. ' Returns: minimum allocation unit of drive, or -1 if
  2081. '          this value couldn't be determined
  2082. '-----------------------------------------------------------
  2083. '
  2084. Private Function GetDrivesAllocUnit(ByVal strDrive As String) As Long
  2085.     Dim lRet As Long
  2086.     Dim lBytes As Long
  2087.     Dim lSect As Long
  2088.     Dim lClust As Long
  2089.     Dim lTot As Long
  2090.  
  2091.     Dim strDriveNew As String
  2092.  
  2093.     On Error Resume Next
  2094.  
  2095.     If GetDrive(strDrive, strDriveNew) Then
  2096.         lRet = GetDiskFreeSpace(strDriveNew, lSect, lBytes, lClust, lTot)
  2097.         If Err.Number = 0 Then
  2098.             If lRet <> 0 Then
  2099.                 GetDrivesAllocUnit = lSect * lBytes
  2100.                 Exit Function
  2101.             End If
  2102.         End If
  2103.     End If
  2104.  
  2105.     MsgError Err.Description & vbLf & vbLf & ResolveResString(resALLOCUNIT) & strDrive, vbExclamation, gstrTitle
  2106. #If SMS Then
  2107.     If gfSMS Then
  2108.         ExitSetup frmSetup1, gintRET_FATAL
  2109.     End If
  2110. #End If
  2111.  
  2112.     GetDrivesAllocUnit = -1
  2113.  
  2114.     Err.Clear
  2115. End Function
  2116.  
  2117. '-----------------------------------------------------------
  2118. ' FUNCTION: GetDrive
  2119. '
  2120. ' Returns True if it finds the drive portion of a path and
  2121. ' False otherwise.
  2122. '
  2123. ' IN: [strPath] - Any local or UNC path. The path must
  2124. ' include at least the drive letter for local drives and the
  2125. ' share for network drives. Following are examples of
  2126. ' allowed syntax:
  2127. '
  2128. ' C, C:, C:setup, C:\, C:\setup, \\server\share,
  2129. ' \\server\share\, \\server\share\sub.
  2130. '
  2131. ' OUT: [strDrive] - If strPath represents a valid drive,
  2132. ' strDrive will return it in this form: '_:\' for local
  2133. ' drives and '\\_\_\' for UNC drives. Either way, it will
  2134. ' always end in a '\' and it will always be lowercase.
  2135. '-----------------------------------------------------------
  2136. '
  2137. Public Function GetDrive(ByVal strPath As String, ByRef strDrive As String) As Boolean
  2138.     Dim lSep As Long
  2139.     Dim lSepLast As Long
  2140.     Dim i As Long
  2141.  
  2142.     lSep = InStr(strPath, gstrSEP_DIR)
  2143.     Select Case lSep
  2144.     Case 3
  2145.         'This must be like 'C:\'
  2146.         If InStr(2, strPath, gstrSEP_DRIVE) = 2 Then
  2147.             'This looks right.
  2148.             strDrive = LCase$(Left$(strPath, 3))
  2149.             GetDrive = True
  2150.         End If
  2151.     Case 2
  2152.         'Can't be a drive
  2153.     Case 1
  2154.         'This must be a UNC because it starts with "\". So, we're looking for
  2155.         '   '\\...\...[\[...]]'.
  2156.         lSep = InStr(2, strPath, gstrSEP_DIR)
  2157.         If lSep = 2 Then
  2158.             'So far, this matches a UNC.
  2159.             lSep = InStr(3, strPath, gstrSEP_DIR)
  2160.             If lSep > 3 Then
  2161.                 'This really looks like a valid UNC.
  2162.                 If lSep < Len(strPath) Then
  2163.                     'So far so good.
  2164.                     lSepLast = InStr(lSep + 1, strPath, gstrSEP_DIR)
  2165.                     Select Case lSepLast
  2166.                     Case Is > lSep + 1
  2167.                         'We've found '\\...\...\'. We're satisfied that this is
  2168.                         '   a valid UNC.
  2169.                         strDrive = LCase$(Left$(strPath, lSepLast))
  2170.                         GetDrive = True
  2171.                     Case 0
  2172.                         'We've found '\\...\...'. We're satisfied that this is a
  2173.                         '   valid UNC.
  2174.                         strDrive = LCase$(strPath & gstrSEP_DIR)
  2175.                         GetDrive = True
  2176.                     End Select
  2177.                 End If
  2178.             End If
  2179.         End If
  2180.     Case Else
  2181.         'We'll allow no '\' if it's like 'C' or 'C:[...]'
  2182.         Select Case Len(strPath)
  2183.         Case 1
  2184.             'This must be like 'C'. Add ':\' and return it.
  2185.             strDrive = LCase$(strPath & gstrSEP_DRIVE & gstrSEP_DIR)
  2186.             GetDrive = True
  2187.         Case Is > 1
  2188.             'This must be like 'C:...'
  2189.             If InStr(2, strPath, gstrSEP_DRIVE) = 2 Then
  2190.                 'This looks like 'C:...'
  2191.                 strDrive = LCase$(Left$(strPath, 2) & gstrSEP_DIR)
  2192.                 GetDrive = True
  2193.             End If
  2194.         End Select
  2195.     End Select
  2196. End Function
  2197.  
  2198. '-----------------------------------------------------------
  2199. ' FUNCTION: GetAppRemovalCmdLine
  2200. '
  2201. ' Returns the correct command-line arguments (including
  2202. ' path to the executable for use in calling the
  2203. ' application removal executable)
  2204. '
  2205. ' IN: [strAppRemovalEXE] - Full path/filename of the app removal EXE
  2206. '     [strAppRemovalLog] - Full path/filename of the app removal logfile
  2207. '     [strSilentLog] - Full path/filename of the file to log messages to when in silent mode.
  2208. '                       If this is an empty string then silent mode is turned off for uninstall.
  2209. '     [fSMS] - Boolean.  If True, we have been doing an SMS install and must tell the Uninstaller
  2210. '              to also do an SMS uninstall.  SMS is the Microsoft Systems Management Server.
  2211. '     [nErrorLevel] - Error level:
  2212. '                        APPREMERR_NONE - no error
  2213. '                        APPREMERR_FATAL - fatal error
  2214. '                        APPREMERR_NONFATAL - non-fatal error, user chose to abort
  2215. '                        APPREMERR_USERCANCEL - user chose to cancel (no error)
  2216. '     [fWaitForParent] - True if the application removal utility should wait
  2217. '                        for the parent (this process) to finish before starting
  2218. '                        to remove files.  Otherwise it may not be able to remove
  2219. '                        this process' executable file, depending upon timing.
  2220. '                        Defaults to False if not specified.
  2221. '-----------------------------------------------------------
  2222. '
  2223. #If SMS Then
  2224. Private Function GetAppRemovalCmdLine(ByVal strAppRemovalEXE As String, ByVal strAppRemovalLog, ByVal strSilentLog As String, ByVal fSMS As Boolean, ByVal nErrorLevel As Integer, Optional fWaitForParent As Boolean = False)
  2225. #Else
  2226. Private Function GetAppRemovalCmdLine(ByVal strAppRemovalEXE As String, ByVal strAppRemovalLog, ByVal strSilentLog As String, ByVal nErrorLevel As Integer, Optional fWaitForParent As Boolean = False)
  2227. #End If
  2228.     Dim strEXE As String
  2229.     Dim strLog As String
  2230.     Dim strSilent As String
  2231.     Dim strErrLevel As String
  2232.     Dim strForce As String
  2233.     Dim strWait As String
  2234.     Dim strSMS As String
  2235.  
  2236.     Dim curProcessId As Currency
  2237.     Dim Wrap As Currency
  2238.     Dim lProcessId As Long
  2239.     Dim cProcessId As Currency
  2240.  
  2241.     strEXE = AddQuotesToFN(strAppRemovalEXE)
  2242.     strLog = "-n " & gstrQUOTE & GetLongPathName(strAppRemovalLog) & gstrQUOTE
  2243.     If gfSilent And Len(strSilentLog) > 0 Then
  2244.         strSilent = "/s " & gstrQUOTE & strSilentLog & gstrQUOTE
  2245.     End If
  2246.     
  2247. #If SMS Then
  2248.     If fSMS Then
  2249.         strSMS = " /q "
  2250.     End If
  2251. #End If
  2252.  
  2253.     If nErrorLevel <> APPREMERR_NONE Then
  2254.         strErrLevel = "-e " & Format$(nErrorLevel)
  2255.         strForce = " -f"
  2256.     End If
  2257.     If fWaitForParent Then
  2258.         Wrap = 2 * (CCur(&H7FFFFFFF) + 1)
  2259.  
  2260.         'Always print as an unsigned long
  2261.         lProcessId = GetCurrentProcessId()
  2262.         cProcessId = lProcessId
  2263.         If cProcessId < 0 Then cProcessId = cProcessId + Wrap
  2264.  
  2265.         strWait = " -w " & str$(cProcessId)
  2266.     End If
  2267.  
  2268.     GetAppRemovalCmdLine = strEXE & " " & strLog & " " & strSilent & " " & strSMS & strErrLevel & strForce & strWait
  2269. End Function
  2270.  
  2271. '-----------------------------------------------------------
  2272. ' FUNCTION: IncrementRefCount
  2273. '
  2274. ' Increments the reference count on a file in the registry
  2275. ' so that it may properly be removed if the user chooses
  2276. ' to remove this application.
  2277. '
  2278. ' IN: [strFullPath] - FULL path/filename of the file
  2279. '     [fFileAlreadyExisted] - indicates whether the given
  2280. '                             file already existed on the
  2281. '                             hard drive
  2282. '-----------------------------------------------------------
  2283. '
  2284. Private Sub IncrementRefCount(ByVal strFullPath As String, ByVal fFileAlreadyExisted As Boolean)
  2285.     Dim strSharedDLLsKey As String
  2286.     Dim fSuccess As Boolean
  2287.     Dim hKey As Long
  2288.     Dim lCurRefCount As Long
  2289.  
  2290.     strSharedDLLsKey = RegPathWinCurrentVersion() & "\SharedDLLs"
  2291.     
  2292.     'We must always use the LFN for the filename, so that we can uniquely
  2293.     'and accurately identify the file in the registry.
  2294.     strFullPath = GetLongPathName(strFullPath)
  2295.     
  2296.     'Get the current reference count for this file
  2297.     fSuccess = RegCreateKey(HKEY_LOCAL_MACHINE, strSharedDLLsKey, vbNullString, hKey)
  2298.     If fSuccess Then
  2299.         If Not RegQueryRefCount(hKey, strFullPath, lCurRefCount) Then
  2300.             'No current reference count for this file
  2301.             If fFileAlreadyExisted Then
  2302.                 'If there was no reference count, but the file was found
  2303.                 'on the hard drive, it means one of two things:
  2304.                 '  1) This file is shipped with the operating system
  2305.                 '  2) This file was installed by an older setup program
  2306.                 '     that does not do reference counting
  2307.                 'In either case, the correct conservative thing to do
  2308.                 'is assume that the file is needed by some application,
  2309.                 'which means it should have a reference count of at
  2310.                 'least 1.  This way, our application removal program
  2311.                 'will not delete this file.
  2312.                 lCurRefCount = 1
  2313.  
  2314.             Else
  2315.                 lCurRefCount = 0
  2316.             End If
  2317.         End If
  2318.         
  2319.         'Increment the count in the registry
  2320.         fSuccess = RegSetNumericValue(hKey, strFullPath, lCurRefCount + 1, False)
  2321.         If Not fSuccess Then
  2322.             GoTo DoErr
  2323.         End If
  2324.         RegCloseKey hKey
  2325.     Else
  2326.         GoTo DoErr
  2327.     End If
  2328.     
  2329. DoErr:
  2330.     'An error message should have already been shown to the user
  2331. End Sub
  2332.  
  2333. '-----------------------------------------------------------
  2334. ' FUNCTION: InitDiskInfo
  2335. '
  2336. ' Called before calculating disk space to initialize
  2337. ' values used/determined when calculating disk space
  2338. ' required.
  2339. '-----------------------------------------------------------
  2340. '
  2341. Public Sub InitDiskInfo()
  2342.     '
  2343.     'Initialize "table" of drives used and disk space array
  2344.     '
  2345.     Set gcolDrivesUsed = New Collection
  2346.     Erase gsDiskSpace
  2347.  
  2348.     mlTotalToCopy = 0
  2349. End Sub
  2350.  
  2351. '-----------------------------------------------------------
  2352. ' FUNCTION: IsDisplayNameUnique
  2353. '
  2354. ' Determines whether a given display name for registering
  2355. '   the application removal executable is unique or not.  This
  2356. '   display name is the title which is presented to the
  2357. '   user in Windows 95's control panel Add/Remove Programs
  2358. '   applet.
  2359. '
  2360. ' IN: [hkeyAppRemoval] - open key to the path in the registry
  2361. '                       containing application removal entries
  2362. '     [strDisplayName] - the display name to test for uniqueness
  2363. '
  2364. ' Returns: True if the given display name is already in use,
  2365. '          False if otherwise
  2366. '-----------------------------------------------------------
  2367. '
  2368. Private Function IsDisplayNameUnique(ByVal hkeyAppRemoval As Long, ByVal strDisplayName As String) As Boolean
  2369.     Dim lIdx As Long
  2370.     Dim strSubkey As String
  2371.     Dim strDisplayNameExisting As String
  2372.     Const strKEY_DISPLAYNAME$ = "DisplayName"
  2373.     Dim hkeyExisting As Long
  2374.  
  2375.     
  2376.     IsDisplayNameUnique = True
  2377.     
  2378.     lIdx = 0
  2379.     Do
  2380.         Select Case RegEnumKey(hkeyAppRemoval, lIdx, strSubkey)
  2381.             Case ERROR_NO_MORE_ITEMS
  2382.                 'No more keys - must be unique
  2383.                 Exit Do
  2384.             Case ERROR_SUCCESS
  2385.                 'We have a key to some application removal program.  Compare its
  2386.                 '  display name with ours
  2387.                 If RegOpenKey(hkeyAppRemoval, strSubkey, hkeyExisting) Then
  2388.                     If RegQueryStringValue(hkeyExisting, strKEY_DISPLAYNAME, strDisplayNameExisting) Then
  2389.                         If UCase$(strDisplayNameExisting) = UCase$(strDisplayName) Then
  2390.                             'There is a match to an existing display name
  2391.                             IsDisplayNameUnique = False
  2392.                             RegCloseKey hkeyExisting
  2393.                             Exit Do
  2394.                         End If
  2395.                     End If
  2396.                     RegCloseKey hkeyExisting
  2397.                 End If
  2398.             Case Else
  2399.                 'Error, we must assume it's unique.  An error will probably
  2400.                 '  occur later when trying to add to the registry
  2401.                 Exit Do
  2402.         End Select
  2403.         lIdx = lIdx + 1
  2404.     Loop
  2405. End Function
  2406.  
  2407. '-----------------------------------------------------------
  2408. ' FUNCTION: IsNewerVer
  2409. '
  2410. ' Compares two file version structures and determines
  2411. ' whether the source file version is newer (greater) than
  2412. ' the destination file version.  This is used to determine
  2413. ' whether a file needs to be installed or not
  2414. '
  2415. ' IN: [sSrcVer] - source file version information
  2416. '     [sDestVer] - dest file version information
  2417. '
  2418. ' Returns: True if source file is newer than dest file,
  2419. '          False if otherwise
  2420. '-----------------------------------------------------------
  2421. '
  2422. Private Function IsNewerVer(sSrcVer As VERINFO, sDestVer As VERINFO) As FileComparison
  2423.     'For the top three version fields, if the source is greater, immediately
  2424.     '   return True. If the destination is greater, immediately return False.
  2425.     '   If they are equal, fall through to check the next field. For the last
  2426.     '   version field, if the source is greater, immediately return True.
  2427.     '   Otherwise, either the destination is newer or the files are the same.
  2428.     '   Either way, immediately return False.
  2429.     IsNewerVer = fcOlder
  2430.     With sSrcVer
  2431.         If .FileVerPart1 > sDestVer.FileVerPart1 Then GoTo INVNewer
  2432.         If .FileVerPart1 < sDestVer.FileVerPart1 Then Exit Function
  2433.  
  2434.         If .FileVerPart2 > sDestVer.FileVerPart2 Then GoTo INVNewer
  2435.         If .FileVerPart2 < sDestVer.FileVerPart2 Then Exit Function
  2436.  
  2437.         If .FileVerPart3 > sDestVer.FileVerPart3 Then GoTo INVNewer
  2438.         If .FileVerPart3 < sDestVer.FileVerPart3 Then Exit Function
  2439.  
  2440.         If .FileVerPart4 > sDestVer.FileVerPart4 Then GoTo INVNewer
  2441.         If .FileVerPart4 = sDestVer.FileVerPart4 Then IsNewerVer = fcEquivalent
  2442.     End With
  2443. 'Either the files are the same or the destination's 4th field is greater. Either
  2444. '   way, return False.
  2445. Exit Function
  2446. INVNewer:
  2447.     IsNewerVer = fcNewer
  2448. End Function
  2449.  
  2450. '-----------------------------------------------------------
  2451. ' FUNCTION: IsValidDestDir
  2452. '
  2453. ' Determines whether or not the destination directory
  2454. ' specifed in the "DefaultDir" key of the [Setup] section
  2455. ' in SETUP.LST or a destination dir entered by the user
  2456. ' is not a subdirectory of the source directory.
  2457. '
  2458. ' Notes: [gstrSrcPath] - points to the source directory
  2459. '        [strDestDir] - points to the dest directory
  2460. '
  2461. ' Returns: True if dest dir is a valid location, False
  2462. '          otherwise
  2463. '-----------------------------------------------------------
  2464. '
  2465. Public Function IsValidDestDir(strDestDir As String) As Integer
  2466.     Dim strMsg As String
  2467.  
  2468.     '
  2469.     ' Both of these paths, strDestDir and gstrSrcPath, are *always*
  2470.     ' in the format 'X:\' or 'X:\DIRNAME\'.
  2471.     '
  2472.     If InStr(UCase$(strDestDir), UCase$(gstrSrcPath)) = 1 Then
  2473.         strMsg = ResolveResString(resDIRSPECIFIED) & vbLf & strDestDir & vbLf & ResolveResString(resSAMEASSRC)
  2474.         MsgFunc strMsg, vbOKOnly Or vbExclamation, gstrTitle
  2475.     Else
  2476.         IsValidDestDir = True
  2477.     End If
  2478. End Function
  2479.  
  2480. '-----------------------------------------------------------
  2481. ' FUNCTION: MakePath
  2482. '
  2483. ' Creates the specified directory path
  2484. '
  2485. ' IN: [strDirName] - name of the dir path to make
  2486. '     [fAllowIgnore] - whether or not to allow the user to
  2487. '                      ignore any encountered errors.  If
  2488. '                      false, the function only returns
  2489. '                      if successful.  If missing, this
  2490. '                      defaults to True.
  2491. '
  2492. ' Returns: True if successful, False if error and the user
  2493. '          chose to ignore.  (The function does not return
  2494. '          if the user selects ABORT/CANCEL on an error.)
  2495. '-----------------------------------------------------------
  2496. '
  2497. Public Function MakePath(ByVal strDir As String, Optional ByVal fAllowIgnore As Boolean = True) As Boolean
  2498.     Dim intButtons As Integer
  2499.     Dim strMsg As String
  2500.     Dim iRet As Integer
  2501.  
  2502.     Do
  2503.         If MakePathAux(strDir) Then
  2504.             MakePath = True
  2505.             Exit Function
  2506.         Else
  2507.             strMsg = ResolveResString(resMAKEDIR, gstrPIPE1, strDir)
  2508.             If fAllowIgnore Then
  2509.                 intButtons = vbAbortRetryIgnore
  2510.             Else
  2511.                 intButtons = vbRetryCancel
  2512.             End If
  2513.             iRet = MsgError(strMsg, intButtons Or vbExclamation Or vbDefaultButton2, gstrSETMSG)
  2514.             '
  2515.             ' if we are running silent then we
  2516.             ' can't continue.  Previous MsgError
  2517.             ' took care of write silent log entry.
  2518.             '
  2519.             If gfNoUserInput Then
  2520.                 ExitSetup frmCopy, gintRET_FATAL
  2521.             End If
  2522.             
  2523.             Select Case iRet
  2524.                 Case vbAbort, vbCancel
  2525.                     ExitSetup frmCopy, gintRET_ABORT
  2526.                 Case vbIgnore
  2527.                     MakePath = False
  2528.                     Exit Function
  2529.                 Case vbRetry
  2530.             End Select
  2531.         End If
  2532.     Loop
  2533. End Function
  2534.  
  2535. '----------------------------------------------------------
  2536. ' SUB: MoveAppRemovalFiles
  2537. '
  2538. ' Moves the app removal logfile to the application directory,
  2539. ' and registers the app removal executable with the operating
  2540. ' system.
  2541. '----------------------------------------------------------
  2542. '
  2543. Public Sub MoveAppRemovalFiles(ByVal strGroupName As String)
  2544.     Dim strNewAppRemovalLogName As String
  2545.     Dim iExt As Integer
  2546.  
  2547.     'Get rid of the cabs in the windows folder.
  2548.     CleanUpCabs
  2549.     'Get rid of the temp dir
  2550.     'Bug fix for #6-34583
  2551.     KillTempFolder
  2552.     'Find a unique name for the app removal logfile in the
  2553.     'application directory
  2554.     
  2555.     '...First try the default extension
  2556.     strNewAppRemovalLogName = gstrDestDir & mstrFILE_APPREMOVALLOGBASE & mstrFILE_APPREMOVALLOGEXT
  2557.     If FileExists(strNewAppRemovalLogName) Then
  2558.         '...Next try incrementing integral extensions
  2559.         Do
  2560.             If iExt > 999 Then
  2561.                 GoTo CopyErr
  2562.             End If
  2563.             
  2564.  
  2565.             strNewAppRemovalLogName = gstrDestDir & mstrFILE_APPREMOVALLOGBASE & gstrSEP_EXT & Format$(iExt, "000")
  2566.             If Not FileExists(strNewAppRemovalLogName) Then
  2567.                 Exit Do 'Unique name was found
  2568.             Else
  2569.                 iExt = iExt + 1
  2570.             End If
  2571.         Loop
  2572.     End If
  2573.  
  2574.     On Error GoTo CopyErr
  2575.     FileCopy gstrAppRemovalLog, strNewAppRemovalLogName
  2576.     
  2577.     'Now we need to start logging in the new logfile, so that the
  2578.     'creation of the application removal icon under NT gets logged.
  2579.     EnableLogging strNewAppRemovalLogName
  2580.  
  2581.     On Error GoTo 0
  2582.     If Not RegisterAppRemovalEXE(gstrAppRemovalEXE, strNewAppRemovalLogName, strGroupName) Then
  2583.         MsgError ResolveResString(resCANTREGISTERAPPREMOVER), vbExclamation Or vbOKOnly, gstrTitle
  2584.         ExitSetup frmSetup1, gintRET_FATAL
  2585.     End If
  2586.     
  2587.     'Now we can delete the original logfile, since we no longer have a reference
  2588.     'to it, and start using the new logfile
  2589.     On Error Resume Next
  2590.     Kill gstrAppRemovalLog
  2591.     
  2592.     'This temporary app removal logfile should no longer be used
  2593.     gstrAppRemovalLog = strNewAppRemovalLogName
  2594.     gfAppRemovalFilesMoved = True
  2595.     
  2596.     Exit Sub
  2597.     
  2598. CleanUpOnErr:
  2599.     On Error Resume Next
  2600.     Kill strNewAppRemovalLogName
  2601.     On Error GoTo 0
  2602.     MsgError ResolveResString(resCANTCOPYLOG, gstrPIPE1, gstrAppRemovalLog), vbExclamation Or vbOKOnly, gstrTitle
  2603.     ExitSetup Screen.ActiveForm, gintRET_FATAL
  2604.     
  2605. CopyErr:
  2606.     Resume CleanUpOnErr
  2607. End Sub
  2608.  
  2609. '----------------------------------------------------------
  2610. ' SUB: CleanUpCabs
  2611. '
  2612. ' Cleans up temporary cab files.
  2613. '----------------------------------------------------------
  2614. '
  2615. Public Sub CleanUpCabs()
  2616.     Dim lCount As Long
  2617.     Dim sCab As String
  2618.     Dim sTemp As String
  2619.  
  2620.     'Get rid of the cab file(s) in the windows dir (if any).
  2621.     'Regardless of how many cabs there are, gsCABFULLNAME will specify
  2622.     '   the name of the first cab.
  2623.     If FileExists(gsCABFULLNAME) Then
  2624.         Kill gsCABFULLNAME
  2625.     End If
  2626.     If gintCabs > 1 Then
  2627.         'If there is more than one cab, then the cabname will end in
  2628.         '   1.cab. Remove this part and add the appropriate number,
  2629.         '   starting with 2.
  2630.         sTemp = Left$(gsCABFULLNAME, Len(gsCABFULLNAME) - 5)
  2631.         lCount = 2
  2632.         Do
  2633.             sCab = sTemp & CStr(lCount) & gstrSEP_EXT & gsINI_CABNAME
  2634.             If FileExists(sCab) Then
  2635.                 Kill sCab
  2636.             Else
  2637.                 Exit Sub
  2638.             End If
  2639.             lCount = lCount + 1
  2640.             'We don't need this check before the first iteration because
  2641.             '   we already checked that gintCabs > 1 and we explicitly
  2642.             '   set lCount = 2.
  2643.             If lCount > gintCabs Then
  2644.                 Exit Sub
  2645.             End If
  2646.         Loop
  2647.     End If
  2648. End Sub
  2649.  
  2650. '-----------------------------------------------------------
  2651. ' SUB: KillTempFolder
  2652. ' BUG FIX #6-34583
  2653. '
  2654. ' Deletes the temporary files stored in the temp folder
  2655. '-----------------------------------------------------------
  2656. '
  2657. Private Sub KillTempFolder()
  2658.     Const sWILD As String = "*.*"
  2659.     Dim sFile As String
  2660.     
  2661.     On Error Resume Next
  2662.     
  2663.     sFile = Dir$(gsTEMPDIR & sWILD, vbHidden Or vbReadOnly Or vbSystem)
  2664.     Do While Len(sFile) > 0
  2665.         SetAttr gsTEMPDIR & sFile, vbNormal
  2666.         Kill gsTEMPDIR & sFile
  2667.         sFile = Dir$
  2668.     Loop
  2669.     RmDir gsTEMPDIR
  2670. End Sub
  2671.  
  2672. '-----------------------------------------------------------
  2673. ' SUB: ParseDateTime
  2674. '
  2675. ' Same as CDate with a string argument, except that it
  2676. ' ignores the current localization settings.  This is
  2677. ' important because SETUP.LST always uses the same
  2678. ' format for dates.
  2679. '
  2680. ' IN: [strDate] - string representing the date in
  2681. '                 the format mm/dd/yy or mm/dd/yyyy
  2682. ' OUT: The date which strDate represents
  2683. '-----------------------------------------------------------
  2684. '
  2685. Private Function ParseDateTime(ByVal strDateTime As String) As Date
  2686.     Dim Var As Variant
  2687.  
  2688.     Var = strDateTime
  2689.     If 0 = VariantChangeTypeEx(VarPtr(Var), VarPtr(Var), &H409, 0, vbDate) Then
  2690.         ParseDateTime = Var
  2691.     Else
  2692.         'Raise same error as CDate
  2693.         Err.Raise 13
  2694.     End If
  2695. End Function
  2696.  
  2697. '-----------------------------------------------------------
  2698. ' SUB: PromptForNextDisk
  2699. '
  2700. ' If the source media is removable or a network connection,
  2701. ' prompts the user to insert the specified disk number
  2702. ' containing the filename which is used to determine that
  2703. ' the correct disk is inserted.
  2704. '
  2705. ' IN: [intDiskNum] - disk number to insert
  2706. '     [strDetectFile] - file to search for to ensure that
  2707. '                       the correct disk was inserted
  2708. '
  2709. ' Notes: [gstrSrcPath] - used to identify the source drive
  2710. '-----------------------------------------------------------
  2711. '
  2712. Private Sub PromptForNextDisk(ByVal intDiskNum As Integer, ByVal strDetectFile As String)
  2713.     Static intDrvType As Integer
  2714.  
  2715.     Dim intRC As Integer
  2716.     Dim strMsg As String
  2717.     Dim strDrive As String
  2718.     Dim strMultDirBaseName As String
  2719.     Dim strDetectPath As String
  2720.  
  2721.     On Error Resume Next
  2722.  
  2723.     strMultDirBaseName = ResolveResString(resCOMMON_MULTDIRBASENAME)
  2724.     '
  2725.     'Get source drive and, if we haven't yet determined it, get the
  2726.     'source drive type
  2727.     '
  2728.     
  2729.     strDrive = Left$(gstrSrcPath, 2)
  2730.     If intDrvType = 0 Then
  2731.         If IsUNCName(strDrive) Then
  2732.             intDrvType = intDRIVE_REMOTE
  2733.             strDrive = gstrSrcPath
  2734.         Else
  2735.             intDrvType = GetDriveType(Asc(strDrive) - 65)
  2736.         End If
  2737.     End If
  2738.  
  2739.     Do While SrcFileMissing(gstrSrcPath, strDetectFile, intDiskNum)
  2740.         Select Case intDrvType
  2741.             Case 0, intDRIVE_REMOVABLE, intDRIVE_CDROM
  2742.                 strMsg = ResolveResString(resINSERT) & vbLf & ResolveResString(resDISK) & Format$(intDiskNum)
  2743.                 strMsg = strMsg & ResolveResString(resINTO) & strDrive
  2744.             Case intDRIVE_REMOTE
  2745.                 strMsg = ResolveResString(resCHKCONNECT) & strDrive
  2746.             Case intDRIVE_FIXED
  2747.                 If DirExists(gstrSrcPath & strMultDirBaseName & Format$(intDiskNum)) Then
  2748.                     strDetectPath = gstrSrcPath & strMultDirBaseName & Format$(intDiskNum)
  2749.                 Else
  2750.                     strDetectPath = gstrSrcPath
  2751.                 End If
  2752.                 strMsg = ResolveResString(resCOMMON_CANTFINDSRCFILE, gstrPIPE1, strDetectPath & gstrSEP_DIR & strDetectFile)
  2753.         End Select
  2754.  
  2755.         Beep
  2756.         intRC = MsgFunc(strMsg, vbOKCancel Or vbExclamation, gstrSETMSG)
  2757.         '
  2758.         ' We should always fail if in silent or sms mode.
  2759.         '
  2760.         If intRC = vbCancel Or gfNoUserInput Then
  2761.             ExitSetup frmCopy, gintRET_EXIT
  2762.         End If
  2763.     Loop
  2764.  
  2765.     gintCurrentDisk = intDiskNum
  2766. End Sub
  2767.  
  2768. '-----------------------------------------------------------
  2769. ' FUNCTION: SrcFileMissing
  2770. '
  2771. ' Tries to locate the file strSrcFile by first looking
  2772. ' in the strSrcDir directory, then in the DISK(x+1)
  2773. ' directory if it exists.
  2774. '
  2775. ' IN: [strSrcDir] - Directory/Path where file should be.
  2776. '     [strSrcFile] - File we are looking for.
  2777. '     [intDiskNum] - Disk number we are expecting file
  2778. '                    to be on.
  2779. '
  2780. ' Returns: True if file not found; otherwise, false
  2781. '-----------------------------------------------------------
  2782. '
  2783. Private Function SrcFileMissing(ByVal strSrcDir As String, ByVal strSrcFile As String, ByVal intDiskNum As Integer) As Boolean
  2784.     Dim fFound As Boolean
  2785.     Dim strMultDirBaseName As String
  2786.     
  2787.     AddDirSep strSrcDir
  2788.     '
  2789.     ' First check to see if it's in the main src directory.
  2790.     ' This would happen if someone copied the contents of
  2791.     ' all the floppy disks to a single directory on the
  2792.     ' hard drive.  We should allow this to work.
  2793.     '
  2794.     ' This test would also let us know if the user inserted
  2795.     ' the wrong floppy disk or if a network connection is
  2796.     ' unavailable.
  2797.     '
  2798.     If FileExists(strSrcDir & strSrcFile) Then
  2799.         fFound = True
  2800.         GoTo doneSFM
  2801.     End If
  2802.     '
  2803.     ' Next try the DISK(x) subdirectory of the main src
  2804.     ' directory.  This would happen if the floppy disks
  2805.     ' were copied into directories named DISK1, DISK2,
  2806.     ' DISK3,..., DISKN, etc.
  2807.     '
  2808.     strMultDirBaseName = ResolveResString(resCOMMON_MULTDIRBASENAME)
  2809.     If FileExists(strSrcDir & ".." & gstrSEP_DIR & strMultDirBaseName & Format$(intDiskNum) & gstrSEP_DIR & strSrcFile) Then
  2810.         fFound = True
  2811.         GoTo doneSFM
  2812.     End If
  2813.     
  2814. doneSFM:
  2815.     SrcFileMissing = Not fFound
  2816. End Function
  2817. '-----------------------------------------------------------
  2818. ' FUNCTION: ReadIniFile
  2819. '
  2820. ' Reads a value from the specified section/key of the
  2821. ' specified .INI file
  2822. '
  2823. ' IN: [strIniFile] - name of .INI file to read
  2824. '     [strSection] - section where key is found
  2825. '     [strKey] - name of key to get the value of
  2826. '
  2827. ' Returns: non-zero terminated value of .INI file key
  2828. '-----------------------------------------------------------
  2829. '
  2830. Public Function ReadIniFile(ByVal strIniFile As String, ByVal strSection As String, ByVal strKey As String) As String
  2831.     Dim strBuffer As String
  2832.  
  2833.     '
  2834.     'If successful read of .INI file, strip any trailing zero returned by the Windows API GetPrivateProfileString
  2835.     '
  2836.     strBuffer = Space$(gintMAX_SIZE)
  2837.  
  2838.     If GetPrivateProfileString(strSection, strKey, vbNullString, strBuffer, gintMAX_SIZE, strIniFile) Then
  2839.         ReadIniFile = StringFromBuffer(strBuffer)
  2840.     End If
  2841. End Function
  2842.  
  2843. '-----------------------------------------------------------
  2844. ' SUB: ReadSetupFileLine
  2845. '
  2846. ' Reads the requested 'FileX=' key from the specified
  2847. ' section of the setup information file (SETUP.LST).
  2848. '
  2849. ' IN: [strSection] - name of section to read from SETUP.LST,
  2850. '                    Ex: "Files"
  2851. '     [intFileNum] - file number index to read
  2852. '
  2853. ' OUT: [sFile] - FILEINFO Type variable that, after parsing,
  2854. '                holds the information for the file
  2855. '                described.
  2856. '
  2857. ' Returns: True if the requested info was successfully read,
  2858. '          False otherwise
  2859. '-----------------------------------------------------------
  2860. '
  2861. Public Function ReadSetupFileLine(ByVal strSection As String, ByVal intFileNum As Integer, sFile As FILEINFO) As Integer
  2862.     Dim strLine As String
  2863.     Dim strMsg As String
  2864.     Dim intOffset As Integer
  2865.     Dim intAnchor As Integer
  2866.     Dim fDone As Integer
  2867.     Dim fErr As Boolean
  2868.     Dim strVersion As String
  2869.     Dim strFilename As String
  2870.  
  2871.     Dim strInitialDestDir As String
  2872.  
  2873.     Dim strShareType As String
  2874.  
  2875.     sFile.fSystem = False
  2876.     sFile.fShared = False
  2877.     sFile.fDestDirRecognizedBySetupExe = False
  2878.     
  2879.     '
  2880.     ' Read the requested line, if unable to read it (Len(strLine) = 0) then exit
  2881.     '
  2882.     strLine = ReadIniFile(gstrSetupInfoFile, strSection, gstrINI_FILE & Format$(intFileNum))
  2883.     If Len(strLine) = 0 Then
  2884.         Exit Function
  2885.     End If
  2886.  
  2887.     '
  2888.     'source file name, ensure it's not a UNC name
  2889.     '
  2890.     intAnchor = 1
  2891.     sFile.strSrcName = strExtractFilenameItem(strLine, intAnchor, fErr)
  2892.     If fErr Then GoTo RSFLError
  2893.  
  2894.     intAnchor = intAnchor + 1 'Skip past the comma
  2895.     
  2896.     '
  2897.     'dest file name, ensure it's not a UNC name
  2898.     '
  2899.     If Left$(sFile.strSrcName, 1) = gstrAT Then
  2900.         sFile.strDestName = Mid$(sFile.strSrcName, 2)
  2901.     Else
  2902.         sFile.strDestName = sFile.strSrcName
  2903.     End If
  2904.     SeparatePathAndFileName sFile.strDestName, , strFilename
  2905.     '
  2906.     'parse and resolve destination directory
  2907.     '
  2908.     intOffset = intGetNextFldOffset(intAnchor, strLine, gstrCOMMA)
  2909.     If intOffset > 0 Then
  2910.         strInitialDestDir = Mid$(strLine, intAnchor, intOffset - intAnchor)
  2911.         If InStr(UCase$(strInitialDestDir), UCase$(gstrWINSYSDESTSYSFILE)) = 1 Then
  2912.             sFile.fSystem = True
  2913.         End If
  2914.         If InStr(UCase$(strInitialDestDir), UCase$(gstrDAODEST)) = 1 Then
  2915.             '
  2916.             ' Special case for DAO destinations.  If there
  2917.             ' are any DAO files, we need to add special
  2918.             ' DAO reg info later.  gfRegDAO tells us to do that.
  2919.             '
  2920.             gfRegDAO = True
  2921.         End If
  2922.         sFile.strDestDir = ResolveDestDir(strInitialDestDir, , sFile.fDestDirRecognizedBySetupExe)
  2923.         If sFile.strDestDir <> "?" Then
  2924.             sFile.strDestDir = ResolveDir(sFile.strDestDir, False, False)
  2925.             If Len(sFile.strDestDir) = 0 Then 'Or IsUNCName(sFile.strDestDir) Then
  2926.                 GoTo RSFLError
  2927.             End If
  2928.         End If
  2929.     Else
  2930.         GoTo RSFLError
  2931.     End If
  2932.  
  2933.     '
  2934.     'file registration information
  2935.     '
  2936.     intAnchor = intOffset + 1
  2937.     intOffset = intGetNextFldOffset(intAnchor, strLine, gstrCOMMA)
  2938.     If intOffset > 0 Then
  2939.         sFile.strRegister = Mid$(strLine, intAnchor, intOffset - intAnchor)
  2940.     Else
  2941.         GoTo RSFLError
  2942.     End If
  2943.  
  2944.     '
  2945.     'Extract file share type
  2946.     '
  2947.     intAnchor = intOffset + 1
  2948.     intOffset = intGetNextFldOffset(intAnchor, strLine, gstrCOMMA)
  2949.     sFile.fShared = False
  2950.     If intOffset > 0 Then
  2951.         strShareType = Mid$(strLine, intAnchor, intOffset - intAnchor)
  2952.         Select Case UCase$(strShareType)
  2953.             Case UCase$(mstrPRIVATEFILE)
  2954.                 sFile.fShared = False
  2955.             Case UCase$(mstrSHAREDFILE)
  2956.                 If sFile.fSystem Then
  2957.                     'A file cannot be both system and shared
  2958.                     GoTo RSFLError
  2959.                 End If
  2960.                 
  2961.                 sFile.fShared = True
  2962.             Case Else
  2963.                 GoTo RSFLError
  2964.         End Select
  2965.     End If
  2966.     
  2967.     '
  2968.     'Extract file date and convert to a date variant
  2969.     '
  2970.     intAnchor = intOffset + 1
  2971.     intOffset = intGetNextFldOffset(intAnchor, strLine, gstrCOMMA)
  2972.     If intOffset > 0 Then
  2973.         On Error GoTo RSFLError
  2974.         sFile.varDate = ParseDateTime(Mid$(strLine, intAnchor, intOffset - intAnchor))
  2975.         On Error GoTo 0
  2976.     End If
  2977.  
  2978.     '
  2979.     'Get file size
  2980.     '
  2981.     intAnchor = intOffset + 1
  2982.     intOffset = intGetNextFldOffset(intAnchor, strLine, gstrCOMMA)
  2983.     If intOffset > 0 Then
  2984.         sFile.lFileSize = Val(Mid$(strLine, intAnchor, intOffset - intAnchor))
  2985.     Else
  2986.         GoTo RSFLError
  2987.     End If
  2988.  
  2989.     '
  2990.     ' Get the version number, otherwise flag that there is no version info
  2991.     '
  2992.     intAnchor = intOffset + 1
  2993.     If intOffset > 0 Then
  2994.         strVersion = Trim$(Right$(strLine, Len(strLine) - intOffset))
  2995.         If Len(strVersion) = 0 Then
  2996.             sFile.sVerInfo.FileVerPart1 = gintNOVERINFO
  2997.         Else
  2998.             PackVerInfo strVersion, sFile.sVerInfo
  2999.         End If
  3000.     Else
  3001.         GoTo RSFLError
  3002.     End If
  3003.     
  3004. RSFLDone:
  3005.     ReadSetupFileLine = True
  3006.     Exit Function
  3007.  
  3008. RSFLError:
  3009.     strMsg = gstrSetupInfoFile & vbLf & vbLf & ResolveResString(resINVLINE) & vbLf & vbLf
  3010.     strMsg = strMsg & ResolveResString(resSECTNAME) & strSection & vbLf & strLine
  3011.     MsgError strMsg, vbCritical, gstrTitle
  3012.     ExitSetup frmSetup1, gintRET_FATAL
  3013. End Function
  3014.  
  3015. '-----------------------------------------------------------
  3016. ' SUB: ReadSetupRemoteLine
  3017. '
  3018. ' Reads the requested 'RemoteX=' key from the specified
  3019. ' section of the setup information file (SETUP.LST).
  3020. '
  3021. ' IN: [strSection] - name of section to read from SETUP.LST,
  3022. '                    Ex: "Files"
  3023. '     [intFileNum] - remote number index to read
  3024. '
  3025. ' OUT: [rInfo] - REGINFO Type variable that, after parsing,
  3026. '                holds the information for the line
  3027. '                described.
  3028. '
  3029. ' Returns: True if the requested info was successfully read,
  3030. '          False otherwise
  3031. '
  3032. ' Notes: Remote server lines in the setup information file
  3033. '        have the following format:
  3034. '
  3035. '        address,protocol,authentication-level
  3036. '
  3037. '        [address] - network address of the server, if known
  3038. '        [protocol] - network protocol name, if known
  3039. '        [authentication level] - authentication level (or 0 for default)
  3040. '-----------------------------------------------------------
  3041. '
  3042. Private Function ReadSetupRemoteLine(ByVal strSection As String, ByVal intFileNum As Integer, rInfo As REGINFO) As Integer
  3043.     Dim strLine As String
  3044.     Dim strMsg As String
  3045.     Dim intAnchor As Integer
  3046.     Dim intOffset As Integer
  3047.     Dim fErr As Boolean
  3048.  
  3049.     Const intMaxAuthentication = 6
  3050.     Dim strAuthentication As String
  3051.  
  3052.     '
  3053.     'Read the requested line, if unable to read it (Len(strLine) = 0) then exit
  3054.     '
  3055.     strLine = ReadIniFile(gstrSetupInfoFile, strSection, gstrINI_REMOTE & Format$(intFileNum))
  3056.     If Len(strLine) = 0 Then
  3057.         Exit Function
  3058.     End If
  3059.  
  3060.     '
  3061.     'Get the network address
  3062.     '
  3063.     intAnchor = 1
  3064.     If Mid$(strLine, intAnchor, 1) = gstrCOMMA Then
  3065.         rInfo.strNetworkAddress = vbNullString
  3066.     Else
  3067.         rInfo.strNetworkAddress = strExtractFilenameItem(strLine, intAnchor, fErr)
  3068.     End If
  3069.     If fErr Then GoTo RSRLError
  3070.     intAnchor = intAnchor + 1 'Skip past the comma
  3071.  
  3072.     '
  3073.     'Get the network protocol
  3074.     '
  3075.     If Mid$(strLine, intAnchor, 1) = gstrCOMMA Then
  3076.         rInfo.strNetworkProtocol = vbNullString
  3077.     Else
  3078.         rInfo.strNetworkProtocol = strExtractFilenameItem(strLine, intAnchor, fErr)
  3079.     End If
  3080.     If fErr Then GoTo RSRLError
  3081.     intAnchor = intAnchor + 1 'Skip past the comma
  3082.  
  3083.     '
  3084.     'Get the authentication level (must be a single digit
  3085.     '  in the range 0..6)
  3086.     '
  3087.     strAuthentication = Mid$(strLine, intAnchor, 1)
  3088.     If Len(strAuthentication) <> 1 Then GoTo RSRLError
  3089.     If (Asc(strAuthentication) < Asc("0")) Or (Asc(strAuthentication) > Asc("9")) Then GoTo RSRLError
  3090.     rInfo.intAuthentication = Val(strAuthentication)
  3091.     If rInfo.intAuthentication > intMaxAuthentication Then GoTo RSRLError
  3092.     '
  3093.     ' Is this dcom or remote automation?
  3094.     '
  3095.     intAnchor = InStr(intAnchor + 1, strLine, gstrCOMMA)
  3096.     If intAnchor > 0 Then
  3097.         rInfo.fDCOM = (UCase$(Trim$(Mid$(strLine, intAnchor + 1))) = gstrDCOM) 'gstrDCOM is uppercase.
  3098.     End If
  3099.     
  3100.     ReadSetupRemoteLine = True
  3101.     Exit Function
  3102.  
  3103. RSRLError:
  3104.     strMsg = gstrSetupInfoFile & vbLf & vbLf & ResolveResString(resINVLINE) & vbLf & vbLf
  3105.     strMsg = strMsg & ResolveResString(resSECTNAME) & strSection & vbLf & strLine
  3106.     MsgError strMsg, vbCritical, gstrTitle
  3107.     ExitSetup frmSetup1, gintRET_FATAL
  3108. End Function
  3109.  
  3110. '-----------------------------------------------------------
  3111. ' FUNCTION: RegCloseKey
  3112. '
  3113. ' Closes an open registry key.
  3114. '
  3115. ' Returns: True on success, else False.
  3116. '-----------------------------------------------------------
  3117. '
  3118. Private Function RegCloseKey(ByVal hKey As Long) As Boolean
  3119.     Dim lResult As Long
  3120.  
  3121.     lResult = OSRegCloseKey(hKey)
  3122.     RegCloseKey = (lResult = ERROR_SUCCESS)
  3123. End Function
  3124.  
  3125. '-----------------------------------------------------------
  3126. ' FUNCTION: RegCreateKey
  3127. '
  3128. ' Opens (creates if already exists) a key in the system registry.
  3129. '
  3130. ' IN: [hkey] - The HKEY parent.
  3131. '     [lpszSubKeyPermanent] - The first part of the subkey of
  3132. '         'hkey' that will be created or opened.  The application
  3133. '         removal utility (32-bit only) will never delete any part
  3134. '         of this subkey.  May NOT be an empty string.
  3135. '     [lpszSubKeyRemovable] - The subkey of hkey\lpszSubKeyPermanent
  3136. '         that will be created or opened.  If the application is
  3137. '         removed (32-bit only), then this entire subtree will be
  3138. '         deleted, if it is empty at the time of application removal.
  3139. '         If this parameter is an empty string, then the entry
  3140. '         will not be logged.
  3141. '
  3142. ' OUT: [phkResult] - The HKEY of the newly-created or -opened key.
  3143. '
  3144. ' Returns: True if the key was created/opened OK, False otherwise
  3145. '   Upon success, phkResult is set to the handle of the key.
  3146. '-----------------------------------------------------------
  3147. '
  3148. Private Function RegCreateKey(ByVal hKey As Long, ByVal lpszSubKeyPermanent As String, ByVal lpszSubKeyRemovable As String, phkResult As Long) As Boolean
  3149.     Dim lResult As Long
  3150.     Dim strHkey As String
  3151.     Dim fLog As Boolean
  3152.     Dim strSubKeyFull As String
  3153.  
  3154.     If Len(lpszSubKeyPermanent) = 0 Then
  3155.         Exit Function
  3156.     End If
  3157.     
  3158.     If Left$(lpszSubKeyRemovable, 1) = "\" Then
  3159.         lpszSubKeyRemovable = Mid$(lpszSubKeyRemovable, 2)
  3160.     End If
  3161.  
  3162.     fLog = (Len(lpszSubKeyRemovable) > 0)
  3163.     
  3164.     If Len(lpszSubKeyRemovable) > 0 Then
  3165.         strSubKeyFull = lpszSubKeyPermanent & "\" & lpszSubKeyRemovable
  3166.     Else
  3167.         strSubKeyFull = lpszSubKeyPermanent
  3168.     End If
  3169.     strHkey = strGetHKEYString(hKey)
  3170.  
  3171.     If fLog Then
  3172.         NewAction _
  3173.           gstrKEY_REGKEY, _
  3174.           gstrQUOTE & strHkey & "\" & lpszSubKeyPermanent & gstrQUOTE _
  3175.             & ", " & gstrQUOTE & lpszSubKeyRemovable & gstrQUOTE
  3176.     End If
  3177.  
  3178.     lResult = OSRegCreateKey(hKey, strSubKeyFull, phkResult)
  3179.     If lResult = ERROR_SUCCESS Then
  3180.         RegCreateKey = True
  3181.         If fLog Then
  3182.             CommitAction
  3183.         End If
  3184.         AddHkeyToCache phkResult, strHkey & "\" & strSubKeyFull
  3185.     Else
  3186.         MsgError ResolveResString(resERR_REG), vbOKOnly Or vbExclamation, gstrTitle
  3187.         If fLog Then
  3188.             AbortAction
  3189.         End If
  3190.         If gfNoUserInput Then
  3191.             ExitSetup frmSetup1, gintRET_FATAL
  3192.         End If
  3193.     End If
  3194. End Function
  3195.  
  3196. '-----------------------------------------------------------
  3197. ' FUNCTION: RegDeleteKey
  3198. '
  3199. ' Deletes an existing key in the system registry.
  3200. '
  3201. ' Returns: True on success, False otherwise
  3202. '-----------------------------------------------------------
  3203. '
  3204. Private Function RegDeleteKey(ByVal hKey As Long, ByVal lpszSubKey As String) As Boolean
  3205.     Dim lResult As Long
  3206.  
  3207.     lResult = OSRegDeleteKey(hKey, lpszSubKey)
  3208.     RegDeleteKey = (lResult = ERROR_SUCCESS)
  3209. End Function
  3210.  
  3211. '-----------------------------------------------------------
  3212. ' SUB: RegEdit
  3213. '
  3214. ' Calls REGEDIT to add the information in the specifed file
  3215. ' to the system registry.  If your .REG file requires path
  3216. ' information based upon the destination directory given by
  3217. ' the user, then you will need to write and call a .REG fixup
  3218. ' routine before performing the registration below.
  3219. '
  3220. ' WARNING: Use of this functionality under Win32 is not recommended,
  3221. ' WARNING: because the application removal utility does not support
  3222. ' WARNING: undoing changes that occur as a result of calling
  3223. ' WARNING: REGEDIT on an arbitrary .REG file.
  3224. ' WARNING: Instead, it is recommended that you use the RegCreateKey(),
  3225. ' WARNING: RegOpenKey(), RegSetStringValue(), etc. functions in
  3226. ' WARNING: this module instead.  These make entries to the
  3227. ' WARNING: application removal logfile, thus enabling application
  3228. ' WARNING: removal to undo such changes.
  3229. '
  3230. ' IN: [strRegFile] - name of file containing reg. info
  3231. '-----------------------------------------------------------
  3232. '
  3233. Private Sub RegEdit(ByVal strRegFile As String)
  3234.     Const strREGEDIT$ = "REGEDIT /S "
  3235.  
  3236.     Dim fShellOK As Integer
  3237.  
  3238.     On Error Resume Next
  3239.  
  3240.     If FileExists(strRegFile) Then
  3241.         strRegFile = AddQuotesToFN(strRegFile)
  3242.         
  3243.         fShellOK = SyncShell(strREGEDIT & strRegFile, INFINITE, , True)
  3244.         frmSetup1.Refresh
  3245.     Else
  3246.         MsgError ResolveResString(resCANTFINDREGFILE, gstrPIPE1, strRegFile), vbExclamation Or vbOKOnly, gstrTitle
  3247.         ExitSetup frmSetup1, gintRET_FATAL
  3248.     End If
  3249.  
  3250.     Err.Clear
  3251. End Sub
  3252.  
  3253. '-----------------------------------------------------------
  3254. ' FUNCTION: RegEnumKey
  3255. '
  3256. ' Enumerates through the subkeys of an open registry
  3257. ' key (returns the "i"th subkey of hkey, if it exists)
  3258. '
  3259. ' Returns:
  3260. '   ERROR_SUCCESS on success.  strSubkeyName is set to the name of the subkey.
  3261. '   ERROR_NO_MORE_ITEMS if there are no more subkeys (32-bit only)
  3262. '   anything else - error
  3263. '-----------------------------------------------------------
  3264. '
  3265. Private Function RegEnumKey(ByVal hKey As Long, ByVal i As Long, strKeyName As String) As Long
  3266.     Dim strResult As String
  3267.     
  3268.     strResult = Space$(300)
  3269.     RegEnumKey = OSRegEnumKey(hKey, i, strResult, Len(strResult))
  3270.     strKeyName = StringFromBuffer(strResult)
  3271. End Function
  3272.  
  3273. '-----------------------------------------------------------
  3274. ' SUB: RegisterDAO
  3275. '
  3276. ' Special keys need to be added to the registry if
  3277. ' DAO is installed.  This routine adds those keys.
  3278. '
  3279. ' Note, these keys will not be uninstalled.
  3280. '-----------------------------------------------------------
  3281. '
  3282. Public Sub RegisterDAO()
  3283.     Const strDAOKey = "CLSID\{F7A9C6E0-EFF2-101A-8185-00DD01108C6B}"
  3284.     Const strDAOKeyVal = "OLE 2.0 Link"
  3285.     Const strDAOInprocHandlerKey = "CLSID\{F7A9C6E0-EFF2-101A-8185-00DD01108C6B}\InprocHandler"
  3286.     Const strDAOInprocHandlerKeyVal = "ole2.dll"
  3287.     Const strDAOProgIDKey = "CLSID\{F7A9C6E0-EFF2-101A-8185-00DD01108C6B}\ProgID"
  3288.     Const strDAOProgIDKeyVal = "Access.OLE2Link"
  3289.     
  3290.     Dim hKey As Long
  3291.     
  3292.     If Not RegCreateKey(HKEY_CLASSES_ROOT, strDAOKey, vbNullString, hKey) Then
  3293.         '
  3294.         ' RegCreateKey displays an error if something goes wrong.
  3295.         '
  3296.         GoTo REGDAOError
  3297.     End If
  3298.     '
  3299.     ' Set the key's value
  3300.     '
  3301.     If Not RegSetStringValue(hKey, vbNullString, strDAOKeyVal, False) Then
  3302.         '
  3303.         ' RegSetStringValue displays an error if something goes wrong.
  3304.         '
  3305.         GoTo REGDAOError
  3306.     End If
  3307.     '
  3308.     ' Close the key
  3309.     '
  3310.     RegCloseKey hKey
  3311.     '
  3312.     ' Repeat the same process for the other two keys.
  3313.     '
  3314.     If Not RegCreateKey(HKEY_CLASSES_ROOT, strDAOInprocHandlerKey, vbNullString, hKey) Then GoTo REGDAOError
  3315.     If Not RegSetStringValue(hKey, vbNullString, strDAOInprocHandlerKeyVal, False) Then GoTo REGDAOError
  3316.     RegCloseKey hKey
  3317.     
  3318.     If Not RegCreateKey(HKEY_CLASSES_ROOT, strDAOProgIDKey, vbNullString, hKey) Then GoTo REGDAOError
  3319.     If Not RegSetStringValue(hKey, vbNullString, strDAOProgIDKeyVal, False) Then GoTo REGDAOError
  3320.     RegCloseKey hKey
  3321.  
  3322.     Exit Sub
  3323.         
  3324. REGDAOError:
  3325.     '
  3326.     ' Error messages should have already been displayed.
  3327.     '
  3328.     ExitSetup frmSetup1, gintRET_FATAL
  3329.         
  3330. End Sub
  3331. '-----------------------------------------------------------
  3332. ' SUB: RegisterFiles
  3333. '
  3334. ' Loop through the list (array) of files to register that
  3335. ' was created in the CopySection function and register
  3336. ' each file therein as required
  3337. '
  3338. ' Notes: msRegInfo() array created by CopySection function
  3339. '-----------------------------------------------------------
  3340. '
  3341. Public Sub RegisterFiles()
  3342.     'This should remain uppercase.
  3343.     Const strEXT_EXE$ = "EXE"
  3344.  
  3345.     Dim intIdx As Integer
  3346.     Dim intLastIdx As Integer
  3347.     Dim strFilename As String
  3348.     Dim strMsg As String
  3349.  
  3350.     Dim intDllSelfRegRet As Integer
  3351.     Dim intErrRes As Integer
  3352.     Const FAIL_OLE = 2
  3353.     Const FAIL_LOAD = 3
  3354.     Const FAIL_ENTRY = 4
  3355.     Const FAIL_REG = 5
  3356.  
  3357.     On Error Resume Next
  3358.  
  3359.     '
  3360.     'Get number of items to register, if none then we can get out of here
  3361.     '
  3362.     intLastIdx = UBound(msRegInfo)
  3363.     If Err.Number <> 0 Then
  3364.         GoTo RFCleanup
  3365.     End If
  3366.  
  3367.     For intIdx = 0 To intLastIdx
  3368.         strFilename = msRegInfo(intIdx).strFilename
  3369.  
  3370.         If UCase$(Extension(msRegInfo(intIdx).strRegister)) = UCase$(gsEXT_REG) Then
  3371.             If UCase$(BaseName(msRegInfo(intIdx).strFilename)) = UCase$(BaseName(msRegInfo(intIdx).strRegister)) Then
  3372.                 Kill msRegInfo(intIdx).strRegister
  3373.             End If
  3374.             GoTo GoodToGo
  3375.         End If
  3376.         Select Case UCase$(msRegInfo(intIdx).strRegister)
  3377.             Case mstrDLLSELFREGISTER
  3378.                 NewAction gstrKEY_DLLSELFREGISTER, gstrQUOTE & strFilename & gstrQUOTE
  3379.                 
  3380. RetryDllSelfReg:
  3381.                 Err.Clear
  3382.                 intErrRes = 0
  3383.                 intDllSelfRegRet = DLLSelfRegister(strFilename)
  3384.                 If (Err.Number <> 49) And (Err.Number <> 0) Then
  3385.                     intErrRes = resCOMMON_CANTREGUNEXPECTED
  3386.                 Else
  3387.                     Select Case intDllSelfRegRet
  3388.                         Case 0
  3389.                             'Good - everything's okay
  3390.                         Case FAIL_OLE
  3391.                             intErrRes = resCOMMON_CANTREGOLE
  3392.                         Case FAIL_LOAD
  3393.                             intErrRes = resCOMMON_CANTREGLOAD
  3394.                         Case FAIL_ENTRY
  3395.                             intErrRes = resCOMMON_CANTREGENTRY
  3396.                         Case FAIL_REG
  3397.                             intErrRes = resCOMMON_CANTREGREG
  3398.                         Case Else
  3399.                             intErrRes = resCOMMON_CANTREGUNEXPECTED
  3400.                     End Select
  3401.                 End If
  3402.                 If intErrRes Then
  3403.                     'There was some kind of error
  3404.                     
  3405.                     'Log the more technical version of the error message -
  3406.                     'this would be too confusing to show to the end user
  3407.                     LogError ResolveResString(intErrRes, gstrPIPE1, strFilename)
  3408.                     
  3409.                     'Now show a general error message to the user
  3410. AskWhatToDo:
  3411.                     strMsg = ResolveResString(resCOMMON_CANTREG, gstrPIPE1, strFilename)
  3412.                     
  3413.                     Select Case MsgError(strMsg, vbExclamation Or vbAbortRetryIgnore, gstrTitle)
  3414.                         Case vbAbort:
  3415.                             ExitSetup frmSetup1, gintRET_ABORT
  3416.                             GoTo AskWhatToDo
  3417.                         Case vbRetry:
  3418.                             GoTo RetryDllSelfReg
  3419.                         Case vbIgnore:
  3420.                             AbortAction
  3421.                     End Select
  3422.                 Else
  3423.                     CommitAction
  3424.                 End If
  3425.             Case mstrEXESELFREGISTER
  3426.                 '
  3427.                 'Only self register EXE files
  3428.                 '
  3429.                 If UCase$(Extension(strFilename)) = strEXT_EXE Then 'strEXT_EXE is uppercase.
  3430.                     NewAction gstrKEY_EXESELFREGISTER, gstrQUOTE & strFilename & gstrQUOTE
  3431.                     Err.Clear
  3432.                     ExeSelfRegister strFilename
  3433.                     If Err.Number <> 0 Then
  3434.                         AbortAction
  3435.                     Else
  3436.                         CommitAction
  3437.                     End If
  3438.                 End If
  3439.             Case mstrREMOTEREGISTER
  3440.                 NewAction gstrKEY_REMOTEREGISTER, gstrQUOTE & strFilename & gstrQUOTE
  3441.                 Err.Clear
  3442.                 RemoteRegister strFilename, msRegInfo(intIdx)
  3443.                 If Err.Number <> 0 Then
  3444.                     AbortAction
  3445.                 Else
  3446.                     CommitAction
  3447.                 End If
  3448.             Case mstrTLBREGISTER
  3449.                 NewAction gstrKEY_TLBREGISTER, gstrQUOTE & strFilename & gstrQUOTE
  3450.                 '
  3451.                 ' Call vb6stkit.dll's RegisterTLB export which calls
  3452.                 ' LoadTypeLib and RegisterTypeLib.
  3453.                 '
  3454. RetryTLBReg:
  3455.                 If Not RegisterTLB(strFilename) Then
  3456.                     '
  3457.                     ' Registration of the TLB file failed.
  3458.                     '
  3459.                     LogError ResolveResString(resCOMMON_CANTREGTLB, gstrPIPE1, strFilename)
  3460. TLBAskWhatToDo:
  3461.                     strMsg = ResolveResString(resCOMMON_CANTREGTLB, gstrPIPE1, strFilename)
  3462.                     
  3463.                     Select Case MsgError(strMsg, vbExclamation Or vbAbortRetryIgnore, gstrTitle)
  3464.                         Case vbAbort:
  3465.                             ExitSetup frmSetup1, gintRET_ABORT
  3466.                             GoTo TLBAskWhatToDo
  3467.                         Case vbRetry:
  3468.                             GoTo RetryTLBReg
  3469.                         Case vbIgnore:
  3470.                             AbortAction
  3471.                     End Select
  3472.                 Else
  3473.                     CommitAction
  3474.                 End If
  3475.             Case mstrVBLREGISTER
  3476.                 '
  3477.                 ' RegisterVBLFile takes care of logging, etc.
  3478.                 '
  3479.  
  3480.                 RegisterVBLFile strFilename
  3481.             Case Else
  3482.                 RegEdit msRegInfo(intIdx).strRegister
  3483.         End Select
  3484. GoodToGo:
  3485.     Next
  3486.  
  3487.  
  3488.     Erase msRegInfo
  3489.  
  3490. RFCleanup:
  3491.     Err.Clear
  3492. End Sub
  3493. '-----------------------------------------------------------
  3494. ' SUB: RegisterLicenses
  3495. '
  3496. ' Find all the setup.lst license entries and register
  3497. ' them.
  3498. '-----------------------------------------------------------
  3499. '
  3500. Public Sub RegisterLicenses()
  3501.     Const strINI_LICENSES = "Licenses"
  3502.     Const strREG_LICENSES = "Licenses"
  3503.     Dim iLic As Integer
  3504.     Dim strLine As String
  3505.     Dim strLicKey As String
  3506.     Dim strLicVal As String
  3507.     Dim iCommaPos As Integer
  3508.     Dim strMsg As String
  3509.     Dim hkeyLicenses As Long
  3510.     Const strCopyright$ = "Licensing: Copying the keys may be a violation of established copyrights."
  3511.  
  3512.     'Make sure the Licenses key exists
  3513.     If Not RegCreateKey(HKEY_CLASSES_ROOT, strREG_LICENSES, vbNullString, hkeyLicenses) Then
  3514.         'RegCreateKey will have already displayed an error message
  3515.         '  if something's wrong
  3516.         ExitSetup frmSetup1, gintRET_FATAL
  3517.     End If
  3518.     If Not RegSetStringValue(hkeyLicenses, vbNullString, strCopyright, False) Then
  3519.         RegCloseKey hkeyLicenses
  3520.         ExitSetup frmSetup1, gintRET_FATAL
  3521.     End If
  3522.     RegCloseKey hkeyLicenses
  3523.     
  3524.     iLic = 1
  3525.     Do
  3526.         strLine = ReadIniFile(gstrSetupInfoFile, strINI_LICENSES, gstrINI_LICENSE & iLic)
  3527.         If Len(strLine) = 0 Then
  3528.             '
  3529.             ' We've got all the licenses.
  3530.             '
  3531.             Exit Sub
  3532.         End If
  3533.         strLine = strUnQuoteString(strLine)
  3534.         '
  3535.         ' We have a license, parse it and register it.
  3536.         '
  3537.         iCommaPos = InStr(strLine, gstrCOMMA)
  3538.         If iCommaPos = 0 Then
  3539.             '
  3540.             ' Looks like the setup.lst file is corrupt.  There should
  3541.             ' always be a comma in the license information that separates
  3542.             ' the license key from the license value.
  3543.             '
  3544.             GoTo RLError
  3545.         End If
  3546.         strLicKey = Left$(strLine, iCommaPos - 1)
  3547.         strLicVal = Mid$(strLine, iCommaPos + 1)
  3548.         
  3549.         RegisterLicense strLicKey, strLicVal
  3550.         
  3551.         iLic = iLic + 1
  3552.     Loop While Len(strLine) > 0
  3553.     Exit Sub
  3554.         
  3555. RLError:
  3556.     strMsg = gstrSetupInfoFile & vbLf & vbLf & ResolveResString(resINVLINE) & vbLf & vbLf
  3557.     strMsg = strMsg & ResolveResString(resSECTNAME) & strINI_LICENSES & vbLf & strLine
  3558.     MsgError strMsg, vbCritical, gstrTitle
  3559.     ExitSetup frmSetup1, gintRET_FATAL
  3560. End Sub
  3561. '-----------------------------------------------------------
  3562. ' SUB: RegisterLicense
  3563. '
  3564. ' Register license information given the key and default
  3565. ' value.  License information always goes into
  3566. ' HKEY_CLASSES_ROOT\Licenses.
  3567. '-----------------------------------------------------------
  3568. '
  3569. Private Sub RegisterLicense(strLicKey As String, strLicVal As String)
  3570.     Const strREG_LICENSES = "Licenses"
  3571.     Dim hKey As Long
  3572.     '
  3573.     ' Create the key
  3574.     '
  3575.     If Not RegCreateKey(HKEY_CLASSES_ROOT, strREG_LICENSES, strLicKey, hKey) Then
  3576.         '
  3577.         ' RegCreateKey displays an error if something goes wrong.
  3578.         '
  3579.         GoTo REGError
  3580.     End If
  3581.     '
  3582.     ' Set the key's value
  3583.     '
  3584.     If Not RegSetStringValue(hKey, vbNullString, strLicVal, True) Then
  3585.         '
  3586.         ' RegSetStringValue displays an error if something goes wrong.
  3587.         '
  3588.         GoTo REGError
  3589.     End If
  3590.     '
  3591.     ' Close the key
  3592.     '
  3593.     RegCloseKey hKey
  3594.  
  3595.     Exit Sub
  3596.         
  3597. REGError:
  3598.     '
  3599.     ' Error messages should have already been displayed.
  3600.     '
  3601.     ExitSetup frmSetup1, gintRET_FATAL
  3602. End Sub
  3603. '-----------------------------------------------------------
  3604. ' SUB: RegisterVBLFile
  3605. '
  3606. ' Register license information in a VB License (vbl) file.
  3607. ' Basically, parse out the license info and then call
  3608. ' RegisterLicense.
  3609. '
  3610. ' If strVBLFile is not a valid VBL file, nothing is
  3611. ' registered.
  3612. '-----------------------------------------------------------
  3613. '
  3614. Private Sub RegisterVBLFile(strVBLFile As String)
  3615.     Dim strLicKey As String
  3616.     Dim strLicVal As String
  3617.     
  3618.     GetLicInfoFromVBL strVBLFile, strLicKey, strLicVal
  3619.     If Len(strLicKey) > 0 Then
  3620.         RegisterLicense strLicKey, strLicVal
  3621.     End If
  3622. End Sub
  3623.  
  3624. '----------------------------------------------------------
  3625. ' SUB: RegisterAppRemovalEXE
  3626. '
  3627. ' Registers the application removal program (Windows 95 only)
  3628. ' or else places an icon for it in the application directory.
  3629. '
  3630. ' Returns True on success, False otherwise.
  3631. '----------------------------------------------------------
  3632. '
  3633. Private Function RegisterAppRemovalEXE(ByVal strAppRemovalEXE As String, ByVal strAppRemovalLog As String, ByVal strGroupName As String) As Boolean
  3634.     On Error GoTo Err
  3635.     
  3636.     Const strREGSTR_VAL_AppRemoval_APPNAMELINE = "ApplicationName"
  3637.     Const strREGSTR_VAL_AppRemoval_DISPLAYNAME = "DisplayName"
  3638.     Const strREGSTR_VAL_AppRemoval_COMMANDLINE = "UninstallString"
  3639.     Const strREGSTR_VAL_AppRemoval_APPTOUNINSTALL = "AppToUninstall"
  3640.  
  3641.     Dim strREGSTR_PATH_UNINSTALL As String
  3642.     Dim strAppRemovalCmdLine As String
  3643.     Dim strMsg As String
  3644.  
  3645.     Dim iAppend As Integer
  3646.     Dim fOk As Boolean
  3647.     Dim hkeyAppRemoval As Long
  3648.     Dim hkeyOurs As Long
  3649.     Dim i As Integer
  3650.  
  3651.     Dim strAppRemovalKey As String
  3652.     Dim strAppRemovalKeyBase As String
  3653.     Dim hkeyTest As Long
  3654.  
  3655.     Dim strDisplayName As String
  3656.     Dim strDisplayNameBase As String
  3657.  
  3658.     strREGSTR_PATH_UNINSTALL = RegPathWinCurrentVersion() & "\Uninstall"
  3659.     
  3660.     'The command-line for the application removal executable is simply the path
  3661.     'for the installation logfile
  3662.     strAppRemovalCmdLine = GetAppRemovalCmdLine(strAppRemovalEXE, strAppRemovalLog, vbNullString, False, APPREMERR_NONE)
  3663.     '
  3664.     ' Make sure that the Removal command line (including path, filename, commandline args, etc.
  3665.     ' is not longer than the max allowed, which is _MAX_PATH.
  3666.     '
  3667.     If Not fCheckFNLength(strAppRemovalCmdLine) Then
  3668.         strMsg = ResolveResString(resCANTCREATEICONPATHTOOLONG) & vbLf & vbLf & ResolveResString(resCHOOSENEWDEST) & vbLf & vbLf & strAppRemovalCmdLine
  3669.         MsgError strMsg, vbOKOnly, gstrSETMSG
  3670.         ExitSetup frmCopy, gintRET_FATAL
  3671.     End If
  3672.     '
  3673.     ' Create registry entries to tell Windows where the app removal executable is,
  3674.     ' how it should be displayed to the user, and what the command-line arguments are
  3675.     '
  3676.     'Go ahead and create a key to the main Uninstall branch
  3677.     If Not RegCreateKey(HKEY_LOCAL_MACHINE, strREGSTR_PATH_UNINSTALL, vbNullString, hkeyAppRemoval) Then
  3678.         GoTo Err
  3679.     End If
  3680.     
  3681.     'We need a unique key.  This key is never shown to the end user.  We will use a key of
  3682.     'the form 'ST5UNST #xxx'
  3683.     strAppRemovalKeyBase = mstrFILE_APPREMOVALLOGBASE$ & " #"
  3684.     iAppend = 1
  3685.     
  3686.     Do
  3687.         strAppRemovalKey = strAppRemovalKeyBase & Format$(iAppend)
  3688.         If RegOpenKey(hkeyAppRemoval, strAppRemovalKey, hkeyTest) Then
  3689.             'This key already exists.  But we need a unique key.
  3690.             RegCloseKey hkeyTest
  3691.         Else
  3692.             'We've found a key that doesn't already exist.  Use it.
  3693.             Exit Do
  3694.         End If
  3695.         
  3696.         iAppend = iAppend + 1
  3697.     Loop
  3698.     
  3699.     '
  3700.     ' We also need a unique displayname.  This name is
  3701.     ' the only means the user has to identify the application
  3702.     ' to remove
  3703.     '
  3704.     strDisplayName = gstrAppName 'First try... Application name
  3705.     If Not IsDisplayNameUnique(hkeyAppRemoval, strDisplayName) Then
  3706.         'Second try... Add path
  3707.         strDisplayName = strDisplayName & " (" & gstrDestDir & ")"
  3708.         If Not IsDisplayNameUnique(hkeyAppRemoval, strDisplayName) Then
  3709.             'Subsequent tries... Append a unique integer
  3710.             strDisplayNameBase = strDisplayName
  3711.             iAppend = 3
  3712.             Do
  3713.                 strDisplayName = strDisplayNameBase & " #" & Format$(iAppend)
  3714.                 If IsDisplayNameUnique(hkeyAppRemoval, strDisplayName) Then
  3715.                     Exit Do
  3716.                 Else
  3717.                     iAppend = iAppend + 1
  3718.                 End If
  3719.             Loop
  3720.         End If
  3721.     End If
  3722.     
  3723.     'Go ahead and fill in entries for the app removal executable
  3724.     If Not RegCreateKey(hkeyAppRemoval, strAppRemovalKey, vbNullString, hkeyOurs) Then
  3725.         GoTo Err
  3726.     End If
  3727.     If Not RegSetStringValue(hkeyOurs, strREGSTR_VAL_AppRemoval_APPNAMELINE, gstrAppExe, False) Then
  3728.         GoTo Err
  3729.     End If
  3730.     If Not RegSetStringValue(hkeyOurs, strREGSTR_VAL_AppRemoval_DISPLAYNAME, strDisplayName, False) Then
  3731.         GoTo Err
  3732.     End If
  3733.     If Not RegSetStringValue(hkeyOurs, strREGSTR_VAL_AppRemoval_COMMANDLINE, strAppRemovalCmdLine, False) Then
  3734.         GoTo Err
  3735.     End If
  3736.     If Len(gstrAppToUninstall) = 0 Then gstrAppToUninstall = gstrAppExe
  3737.     If Not RegSetStringValue(hkeyOurs, strREGSTR_VAL_AppRemoval_APPTOUNINSTALL, gstrAppToUninstall, False) Then
  3738.         GoTo Err
  3739.     End If
  3740.     
  3741.     RegCloseKey hkeyAppRemoval
  3742.     RegCloseKey hkeyOurs
  3743.     
  3744.     RegisterAppRemovalEXE = True
  3745.     Exit Function
  3746.     
  3747. Err:
  3748.     If hkeyOurs Then
  3749.         RegCloseKey hkeyOurs
  3750.         RegDeleteKey hkeyAppRemoval, strAppRemovalKey
  3751.     End If
  3752.     If hkeyAppRemoval Then
  3753.         RegCloseKey hkeyAppRemoval
  3754.     End If
  3755. End Function
  3756.  
  3757. '-----------------------------------------------------------
  3758. ' FUNCTION: RegOpenKey
  3759. '
  3760. ' Opens an existing key in the system registry.
  3761. '
  3762. ' Returns: True if the key was opened OK, False otherwise
  3763. '   Upon success, phkResult is set to the handle of the key.
  3764. '-----------------------------------------------------------
  3765. '
  3766. Private Function RegOpenKey(ByVal hKey As Long, ByVal lpszSubKey As String, phkResult As Long) As Boolean
  3767.     Dim lResult As Long
  3768.     Dim strHkey As String
  3769.  
  3770.     strHkey = strGetHKEYString(hKey)
  3771.  
  3772.     lResult = OSRegOpenKey(hKey, lpszSubKey, phkResult)
  3773.     If lResult = ERROR_SUCCESS Then
  3774.         RegOpenKey = True
  3775.         AddHkeyToCache phkResult, strHkey & "\" & lpszSubKey
  3776.     End If
  3777. End Function
  3778.  
  3779. '----------------------------------------------------------
  3780. ' FUNCTION: RegPathWinPrograms
  3781. '
  3782. ' Returns the name of the registry key
  3783. ' "\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
  3784. '----------------------------------------------------------
  3785. '
  3786. Private Function RegPathWinPrograms() As String
  3787.     RegPathWinPrograms = RegPathWinCurrentVersion() & "\Explorer\Shell Folders"
  3788. End Function
  3789.  
  3790. '----------------------------------------------------------
  3791. ' FUNCTION: RegPathWinCurrentVersion
  3792. '
  3793. ' Returns the name of the registry key
  3794. ' "\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion"
  3795. '----------------------------------------------------------
  3796. '
  3797. Private Function RegPathWinCurrentVersion() As String
  3798.     RegPathWinCurrentVersion = "SOFTWARE\Microsoft\Windows\CurrentVersion"
  3799. End Function
  3800.  
  3801. '-----------------------------------------------------------
  3802. ' FUNCTION: RegQueryStringValue
  3803. '
  3804. ' Retrieves the string data for a named
  3805. ' (strValueName = name) or unnamed (Len(strValueName) = 0)
  3806. ' value within a registry key.  If the named value
  3807. ' exists, but its data is not a string, this function
  3808. ' fails.
  3809. '
  3810. ' Returns: True on success, else False.
  3811. '   On success, strData is set to the string data value
  3812. '-----------------------------------------------------------
  3813. '
  3814. Private Function RegQueryStringValue(ByVal hKey As Long, ByVal strValueName As String, strData As String) As Boolean
  3815.     Dim lResult As Long
  3816.     Dim lValueType As Long
  3817.     Dim strBuf As String
  3818.     Dim lDataBufSize As Long
  3819.     
  3820.     ' Get length/data type
  3821.     lResult = OSRegQueryValueEx(hKey, strValueName, 0&, lValueType, ByVal 0&, lDataBufSize)
  3822.     If lResult = ERROR_SUCCESS Then
  3823.         If lValueType = REG_SZ Then
  3824.             strBuf = Space$(lDataBufSize)
  3825.             lResult = OSRegQueryValueEx(hKey, strValueName, 0&, 0&, ByVal strBuf, lDataBufSize)
  3826.             If lResult = ERROR_SUCCESS Then
  3827.                 RegQueryStringValue = True
  3828.                 strData = StringFromBuffer(strBuf)
  3829.             End If
  3830.         End If
  3831.     End If
  3832. End Function
  3833.  
  3834. '----------------------------------------------------------
  3835. ' FUNCTION: RegQueryRefCount
  3836. '
  3837. ' Retrieves the data inteded as a reference count for a
  3838. ' particular value within a registry key.  Although
  3839. ' REG_DWORD is the preferred way of storing reference
  3840. ' counts, it is possible that some installation programs
  3841. ' may incorrect use a string or binary value instead.
  3842. ' This routine accepts the data whether it is a string,
  3843. ' a binary value or a DWORD (Long).
  3844. '
  3845. ' NOTE: There is no 16-bit version of this function.
  3846. '
  3847. ' Returns: True on success, else False.
  3848. '   On success, lrefcount is set to the numeric data value
  3849. '-----------------------------------------------------------
  3850. '
  3851. Private Function RegQueryRefCount(ByVal hKey As Long, ByVal strValueName As String, lRefCount As Long) As Boolean
  3852.     Dim lResult As Long
  3853.     Dim lValueType As Long
  3854.     Dim lBuf As Long
  3855.     Dim lDataBufSize As Long
  3856.     Dim strRefCount As String
  3857.  
  3858.     ' Get length/data type
  3859.     lDataBufSize = 4
  3860.  
  3861.     lResult = OSRegQueryValueEx(hKey, strValueName, 0&, lValueType, lBuf, lDataBufSize)
  3862.     If lResult = ERROR_SUCCESS Then
  3863.         Select Case lValueType
  3864.             Case REG_DWORD
  3865.                 lRefCount = lBuf
  3866.                 RegQueryRefCount = True
  3867.             Case REG_BINARY
  3868.                 If lDataBufSize = 4 Then
  3869.                     lRefCount = lBuf
  3870.                     RegQueryRefCount = True
  3871.                 End If
  3872.             Case REG_SZ
  3873.                 If RegQueryStringValue(hKey, strValueName, strRefCount) Then
  3874.                     lRefCount = Val(strRefCount)
  3875.                     RegQueryRefCount = True
  3876.                 End If
  3877.         End Select
  3878.     End If
  3879. End Function
  3880.  
  3881. '-----------------------------------------------------------
  3882. ' FUNCTION: RegSetNumericValue
  3883. '
  3884. ' Associates a named (strValueName = name) or unnamed (Len(strValueName) = 0)
  3885. '   value with a registry key.
  3886. '
  3887. ' If fLog is missing or is True, then this action is logged in the logfile,
  3888. ' and the value will be deleted by the application removal utility if the
  3889. ' user choose to remove the installed application.
  3890. '
  3891. ' NOTE: There is no 16-bit version of this function.
  3892. '
  3893. ' Returns: True on success, else False.
  3894. '-----------------------------------------------------------
  3895. '
  3896. Private Function RegSetNumericValue(ByVal hKey As Long, ByVal strValueName As String, ByVal lData As Long, Optional ByVal fLog As Boolean = True) As Boolean
  3897.     Dim lResult As Long
  3898.     Dim strHkey As String
  3899.  
  3900.     strHkey = strGetHKEYString(hKey)
  3901.     
  3902.     If fLog Then
  3903.         NewAction _
  3904.           gstrKEY_REGVALUE, _
  3905.           gstrQUOTE & strHkey & gstrQUOTE _
  3906.             & ", " & gstrQUOTE & strValueName & gstrQUOTE
  3907.     End If
  3908.  
  3909.     lResult = OSRegSetValueNumEx(hKey, strValueName, 0, REG_DWORD, lData, 4)
  3910.     If lResult = ERROR_SUCCESS Then
  3911.         RegSetNumericValue = True
  3912.         If fLog Then
  3913.             CommitAction
  3914.         End If
  3915.     Else
  3916.         MsgError ResolveResString(resERR_REG), vbOKOnly Or vbExclamation, gstrTitle
  3917.         If fLog Then
  3918.             AbortAction
  3919.         End If
  3920.         If gfNoUserInput Then
  3921.             ExitSetup frmSetup1, gintRET_FATAL
  3922.         End If
  3923.     End If
  3924. End Function
  3925.  
  3926. '-----------------------------------------------------------
  3927. ' FUNCTION: RegSetStringValue
  3928. '
  3929. ' Associates a named (strValueName = name) or unnamed (Len(strValueName) = 0)
  3930. '   value with a registry key.
  3931. '
  3932. ' If fLog is missing or is True, then this action is logged in the
  3933. ' logfile, and the value will be deleted by the application removal
  3934. ' utility if the user choose to remove the installed application.
  3935. '
  3936. ' Returns: True on success, else False.
  3937. '-----------------------------------------------------------
  3938. '
  3939. Private Function RegSetStringValue(ByVal hKey As Long, ByVal strValueName As String, ByVal strData As String, Optional ByVal fLog As Boolean = True) As Boolean
  3940.     Dim lResult As Long
  3941.     Dim strHkey As String
  3942.     
  3943.     If hKey = 0 Then
  3944.         Exit Function
  3945.     End If
  3946.     
  3947.     strHkey = strGetHKEYString(hKey)
  3948.  
  3949.     If fLog Then
  3950.         NewAction _
  3951.           gstrKEY_REGVALUE, _
  3952.           gstrQUOTE & strHkey & gstrQUOTE _
  3953.             & ", " & gstrQUOTE & strValueName & gstrQUOTE
  3954.     End If
  3955.  
  3956.     lResult = OSRegSetValueEx(hKey, strValueName, 0&, REG_SZ, ByVal strData, LenB(StrConv(strData, vbFromUnicode)) + 1)
  3957.     'lResult = OSRegSetValueEx(hKey, strValueName, 0&, REG_SZ, ByVal strData, Len(strData) + 1)
  3958.     
  3959.     If lResult = ERROR_SUCCESS Then
  3960.         RegSetStringValue = True
  3961.         If fLog Then
  3962.             CommitAction
  3963.         End If
  3964.     Else
  3965.         MsgError ResolveResString(resERR_REG), vbOKOnly Or vbExclamation, gstrTitle
  3966.         If fLog Then
  3967.             AbortAction
  3968.         End If
  3969.         If gfNoUserInput Then
  3970.             ExitSetup frmSetup1, gintRET_FATAL
  3971.         End If
  3972.     End If
  3973. End Function
  3974.  
  3975. '-----------------------------------------------------------
  3976. ' SUB: RemoteRegister
  3977. '
  3978. ' Synchronously run the client registration utility on the
  3979. ' given remote server registration file in order to set it
  3980. ' up properly in the registry.
  3981. '
  3982. ' IN: [strFileName] - .EXE file to register
  3983. '-----------------------------------------------------------
  3984. '
  3985. Private Sub RemoteRegister(ByVal strFilename As String, rInfo As REGINFO)
  3986.     Const strClientRegistrationUtility$ = "CLIREG32.EXE"
  3987.     Const strAddressSwitch = " /s "
  3988.     Const strProtocolSwitch = " /p "
  3989.     Const strSilentSwitch = " /q "
  3990.     Const strNoLogoSwitch = " /nologo "
  3991.     Const strAuthenticationSwitch = " /a "
  3992.     Const strTypelibSwitch = " /t "
  3993.     Const strDCOMSwitch = " /d "
  3994.     Const strEXT_REMOTE$ = "VBR" 'This should remain uppercase
  3995.     Const strEXT_REMOTETLB$ = "TLB"
  3996.  
  3997.     Dim strAddress As String
  3998.     Dim strProtocol As String
  3999.     Dim intAuthentication As Integer
  4000.     Dim strCmdLine As String
  4001.     Dim fShell As Integer
  4002.     Dim strMatchingTLB As String
  4003.     Dim fDCOM As Boolean
  4004.  
  4005.     'Find the name of the matching typelib file.  This should have already
  4006.     'been installed to the same directory as the .VBR file.
  4007.     strMatchingTLB = strFilename
  4008.     If UCase$(Right$(strMatchingTLB, Len(strEXT_REMOTE))) = strEXT_REMOTE Then 'strEXT_REMOTE is uppercase.
  4009.         strMatchingTLB = Left$(strMatchingTLB, Len(strMatchingTLB) - Len(strEXT_REMOTE))
  4010.     End If
  4011.     strMatchingTLB = strMatchingTLB & strEXT_REMOTETLB
  4012.  
  4013.     strAddress = rInfo.strNetworkAddress
  4014.     strProtocol = rInfo.strNetworkProtocol
  4015.     intAuthentication = rInfo.intAuthentication
  4016.     fDCOM = rInfo.fDCOM
  4017.     frmRemoteServerDetails.GetServerDetails strFilename, strAddress, strProtocol, fDCOM
  4018.     frmMessage.Refresh
  4019.     strCmdLine = strClientRegistrationUtility & strAddressSwitch & _
  4020.                  gstrQUOTE & strAddress & gstrQUOTE & " "
  4021.     If Not fDCOM Then
  4022.         strCmdLine = strCmdLine & strProtocolSwitch & strProtocol & _
  4023.                      strAuthenticationSwitch & Format$(intAuthentication) & " "
  4024.     End If
  4025.     strCmdLine = strCmdLine & strNoLogoSwitch & _
  4026.                  strTypelibSwitch & gstrQUOTE & strMatchingTLB & gstrQUOTE & " "
  4027.     If fDCOM Then
  4028.         strCmdLine = strCmdLine & strDCOMSwitch
  4029.     End If
  4030.     If gfNoUserInput Then
  4031.         strCmdLine = strCmdLine & strSilentSwitch
  4032.     End If
  4033.     strCmdLine = strCmdLine & gstrQUOTE & strFilename & gstrQUOTE
  4034.  
  4035.     '
  4036.     'Synchronously shell out and run the utility with the correct switches
  4037.     '
  4038.     fShell = SyncShell(strCmdLine, INFINITE, , False)
  4039.  
  4040.     If Not fShell Then
  4041.         MsgError ResolveResString(resCANTRUNPROGRAM, gstrPIPE1, strClientRegistrationUtility), vbOKOnly Or vbExclamation, gstrTitle, gintRET_FATAL
  4042.         ExitSetup frmSetup1, gintRET_FATAL
  4043.     End If
  4044. End Sub
  4045.  
  4046. '-----------------------------------------------------------
  4047. ' FUNCTION: ResolveDestDir
  4048. '
  4049. ' Given a destination directory string, equate any macro
  4050. ' portions of the string to their runtime determined
  4051. ' actual locations and return a string reflecting the
  4052. ' actual path.
  4053. '
  4054. ' IN: [strDestDir] - string containing directory macro info
  4055. '                    and/or actual dir path info
  4056. '
  4057. '     [fAssumeDir] - boolean that if true, causes this routine
  4058. '                    to assume that strDestDir contains a dir
  4059. '                    path.  If a directory isn't given it will
  4060. '                    make it the application path.  If false,
  4061. '                    this routine will return strDestDir as
  4062. '                    is after performing expansion.  Set this
  4063. '                    to False when you are not sure it is a
  4064. '                    directory but you want to expand macros
  4065. '                    if it contains any.  E.g., If this is a
  4066. '                    command line parameter, you can't be
  4067. '                    certain if it refers to a path.  In this
  4068. '                    case, set fAssumeDir = False.  Default
  4069. '                    is True.
  4070. '
  4071. ' Return: A string containing the resolved dir name
  4072. '-----------------------------------------------------------
  4073. '
  4074. Public Function ResolveDestDir(ByVal strDestDir As String, Optional fAssumeDir As Boolean = True, Optional fRecognizedBySetupExe As Boolean = False) As String
  4075.     Const strMACROSTART$ = "$("
  4076.     Const strMACROEND$ = ")"
  4077.  
  4078.     Dim intPos As Integer
  4079.     Dim strResolved As String
  4080.     Dim hKey As Long
  4081.     Dim strPathsKey As String
  4082.     Dim fQuoted As Boolean
  4083.     
  4084.     Const strProgramFilesKey = "ProgramFilesDir"
  4085.  
  4086.     Dim strCommonFiles As String
  4087.  
  4088.     strPathsKey = RegPathWinCurrentVersion()
  4089.     strDestDir = Trim$(strDestDir)
  4090.     '
  4091.     ' If strDestDir is quoted when passed to this routine, it
  4092.     ' should be quoted when it's returned.  The quotes need
  4093.     ' to be temporarily removed, though, for processing.
  4094.     '
  4095.     If Left$(strDestDir, 1) = gstrQUOTE Then
  4096.         fQuoted = True
  4097.         strDestDir = strUnQuoteString(strDestDir)
  4098.     End If
  4099.     '
  4100.     ' We take the first part of destdir, and if its $( then we need to get the portion
  4101.     ' of destdir up to and including the last paren.  We then test against this for
  4102.     ' macro expansion.  If no ) is found after finding $(, then must assume that it's
  4103.     ' just a normal file name and do no processing.  Only enter the case statement
  4104.     ' if strDestDir starts with $(.
  4105.     '
  4106.     If Left$(strDestDir, 2) = strMACROSTART Then
  4107.         intPos = InStr(strDestDir, strMACROEND)
  4108.  
  4109.         Select Case UCase$(Left$(strDestDir, intPos))
  4110.             Case UCase$(gstrAPPDEST)
  4111.                 If Len(gstrDestDir) > 0 Then
  4112.                     strResolved = gstrDestDir
  4113.                 Else
  4114.                     strResolved = "?"
  4115.                 End If
  4116.             Case UCase$(gstrWINDEST)
  4117.                 strResolved = gstrWinDir
  4118.                 fRecognizedBySetupExe = True
  4119.             Case UCase$(gstrFONTDEST)
  4120.                 strResolved = gstrFontDir
  4121.             Case UCase$(gstrWINSYSDEST), UCase$(gstrWINSYSDESTSYSFILE)
  4122.                 strResolved = gstrWinSysDir
  4123.                 fRecognizedBySetupExe = True
  4124.             Case UCase$(gstrPROGRAMFILES)
  4125.                 If RegOpenKey(HKEY_LOCAL_MACHINE, strPathsKey, hKey) Then
  4126.                     RegQueryStringValue hKey, strProgramFilesKey, strResolved
  4127.                     RegCloseKey hKey
  4128.                 End If
  4129.     
  4130.                 If Len(strResolved) = 0 Then
  4131.                     'If not otherwise set, let strResolved be the root of the first fixed disk
  4132.                     strResolved = strRootDrive()
  4133.                 End If
  4134.             Case UCase$(gstrCOMMONFILES)
  4135.                 'First determine the correct path of Program Files\Common Files, if under Win95
  4136.                 strResolved = strGetCommonFilesPath()
  4137.                 If Len(strResolved) = 0 Then
  4138.                     'If not otherwise set, let strResolved be the Windows directory
  4139.                     strResolved = gstrWinDir
  4140.                 End If
  4141.             Case UCase$(gstrCOMMONFILESSYS)
  4142.                 'First determine the correct path of Program Files\Common Files, if under Win95
  4143.                 strCommonFiles = strGetCommonFilesPath()
  4144.                 If Len(strCommonFiles) > 0 Then
  4145.                     'Okay, now just add \System, and we're done
  4146.                     strResolved = strCommonFiles & "System\"
  4147.                 Else
  4148.                     'If Common Files isn't in the registry, then map the
  4149.                     'entire macro to the Windows\{system,system32} directory
  4150.                     strResolved = gstrWinSysDir
  4151.                 End If
  4152.             Case UCase$(gstrDAODEST)
  4153.                 strResolved = strGetDAOPath()
  4154.             Case Else
  4155.                 intPos = 0
  4156.         End Select
  4157.     End If
  4158.     
  4159.     If intPos <> 0 Then
  4160.         AddDirSep strResolved
  4161.     End If
  4162.  
  4163.     If fAssumeDir Then
  4164.         If intPos = 0 Then
  4165.             '
  4166.             'if no drive spec, and doesn't begin with any root path indicator ("\"),
  4167.             'then we assume that this destination is relative to the app dest dir
  4168.             '
  4169.             If Mid$(strDestDir, 2, 1) <> gstrCOLON Then
  4170.                 If Left$(strDestDir, 1) <> gstrSEP_DIR Then
  4171.                     strResolved = gstrDestDir
  4172.                 End If
  4173.             End If
  4174.         Else
  4175.             If Mid$(strDestDir, intPos + 1, 1) = gstrSEP_DIR Then
  4176.                 intPos = intPos + 1
  4177.             End If
  4178.         End If
  4179.     End If
  4180.  
  4181.     If fQuoted Then
  4182.         ResolveDestDir = strQuoteString(strResolved & Mid$(strDestDir, intPos + 1), True, False)
  4183.     Else
  4184.         ResolveDestDir = strResolved & Mid$(strDestDir, intPos + 1)
  4185.     End If
  4186. End Function
  4187. '-----------------------------------------------------------
  4188. ' FUNCTION: ResolveDestDirs
  4189. '
  4190. ' Given a space delimited string, this routine finds all
  4191. ' Destination directory macros and expands them by making
  4192. ' repeated calls to ResolveDestDir.  See ResolveDestDir.
  4193. '
  4194. ' Note that the macro must immediately follow a space (or
  4195. ' a space followed by a quote) delimiter or else it will
  4196. ' be ignored.
  4197. '
  4198. ' Note that this routine does not assume that each item
  4199. ' in the delimited string is actually a directory path.
  4200. ' Therefore, the last parameter in the call to ResolveDestDir,
  4201. ' below, is false.
  4202. '
  4203. ' IN: [str] - string containing directory macro(s) info
  4204. '             and/or actual dir path info
  4205. '
  4206. ' Return: str with destdir macros expanded.
  4207. '-----------------------------------------------------------
  4208. '
  4209. Private Function ResolveDestDirs(str As String)
  4210.     Dim intAnchor As Integer
  4211.     Dim intOffset As Integer
  4212.     Dim strField As String
  4213.     Dim strExpField As String
  4214.     Dim strExpanded As String
  4215.     
  4216.     If Len(Trim$(strUnQuoteString(str))) = 0 Then
  4217.         ResolveDestDirs = str
  4218.         Exit Function
  4219.     End If
  4220.  
  4221.     intAnchor = 1
  4222.  
  4223.     Do
  4224.         intOffset = intGetNextFldOffset(intAnchor, str, " ")
  4225.         If intOffset = 0 Then intOffset = Len(str) + 1
  4226.         strField = Mid$(str, intAnchor, intOffset - intAnchor)
  4227.         strExpField = ResolveDestDir(strField, False)
  4228.         strExpanded = strExpanded & strExpField & " "
  4229.         intAnchor = intOffset + 1
  4230.     Loop While intAnchor < Len(str)
  4231.     
  4232.     ResolveDestDirs = Trim$(strExpanded)
  4233. End Function
  4234. '-----------------------------------------------------------
  4235. ' FUNCTION: ResolveDir
  4236. '
  4237. ' Given a pathname, resolve it to its smallest form.  If
  4238. ' the pathname is invalid, then optionally warn the user.
  4239. '
  4240. ' IN: [strPathName] - pathname to resolve
  4241. '     [fMustExist] - enforce that the path actually exists
  4242. '     [fWarn] - If True, warn user upon invalid path
  4243. '
  4244. ' Return: A string containing the resolved dir name
  4245. '-----------------------------------------------------------
  4246. '
  4247. Public Function ResolveDir(ByVal strPathName As String, fMustExist As Integer, fWarn As Integer) As String
  4248.     Dim strMsg As String
  4249.     Dim fInValid As Integer
  4250.     Dim strUnResolvedPath As String
  4251.     Dim strResolvedPath As String
  4252.     Dim nIgnore As Long
  4253.     Dim cbResolved As Long
  4254.  
  4255.     Dim strDummy As String
  4256.  
  4257.     On Error Resume Next
  4258.     '
  4259.     'If the pathname is in actuality a file name, then it's invalid
  4260.     '
  4261.     If FileExists(strPathName) Then
  4262.         fInValid = True
  4263.         GoTo RDContinue
  4264.     End If
  4265.  
  4266.     strUnResolvedPath = strPathName
  4267.  
  4268.     If InStr(3, strUnResolvedPath, gstrSEP_DIR) > 0 Then
  4269.  
  4270.         strResolvedPath = Space$(gintMAX_PATH_LEN)
  4271.         cbResolved = GetFullPathName(strUnResolvedPath, gintMAX_PATH_LEN, strResolvedPath, nIgnore)
  4272.         If cbResolved = 0 Then
  4273.             '
  4274.             ' The path couldn't be resolved.  If we can actually
  4275.             ' switch to the directory we want, continue anyway.
  4276.             '
  4277.             AddDirSep strUnResolvedPath
  4278.             If DirExists(strUnResolvedPath) Then
  4279.                 strResolvedPath = strUnResolvedPath
  4280.             Else
  4281.                 fInValid = True
  4282.             End If
  4283.         Else
  4284.             '
  4285.             ' GetFullPathName returned us a NULL terminated string in
  4286.             ' strResolvedPath.  Remove the NULL.
  4287.             '
  4288.             strResolvedPath = StringFromBuffer(strResolvedPath)
  4289.             If Not CheckDrive(strResolvedPath, gstrTitle) Then
  4290.                 fInValid = True
  4291.             Else
  4292.                 AddDirSep strResolvedPath
  4293.                 If fMustExist Then
  4294.                     Err.Clear
  4295.                     
  4296.                     strDummy = Dir$(strResolvedPath & "*.*")
  4297.                     
  4298.                     If Err.Number <> 0 Then
  4299.                         strMsg = ResolveResString(resNOTEXIST) & vbLf & vbLf
  4300.                         fInValid = True
  4301.                     End If
  4302.                 End If
  4303.             End If
  4304.         End If
  4305.     Else
  4306.         fInValid = True
  4307.     End If
  4308.  
  4309. RDContinue:
  4310.     If fInValid Then
  4311.         If fWarn Then
  4312.             strMsg = strMsg & ResolveResString(resDIRSPECIFIED) & vbLf & vbLf & strPathName & vbLf & vbLf
  4313.             strMsg = strMsg & ResolveResString(resDIRINVALID)
  4314.             MsgError strMsg, vbOKOnly Or vbExclamation, ResolveResString(resDIRINVNAME)
  4315.             If gfNoUserInput Then
  4316.                 ExitSetup frmSetup1, gintRET_FATAL
  4317.             End If
  4318.         End If
  4319.  
  4320.         ResolveDir = vbNullString
  4321.     Else
  4322.         ResolveDir = strResolvedPath
  4323.     End If
  4324.  
  4325.     Err.Clear
  4326. End Function
  4327.  
  4328. '-----------------------------------------------------------
  4329. ' SUB: ShowPathDialog
  4330. '
  4331. ' Display form to allow user to get either a source or
  4332. ' destination path
  4333. '-----------------------------------------------------------
  4334. '
  4335. Public Sub ShowPathDialog()
  4336.     '
  4337.     'frmPath.Form_Load() reads frmSetup1.Tag to determine whether
  4338.     'this is a request for the source or destination path
  4339.     '
  4340.     frmPath.Show vbModal
  4341.  
  4342.     If gintRetVal = gintRET_CONT Then
  4343.         gstrDestDir = frmSetup1.Tag
  4344.     End If
  4345. End Sub
  4346.  
  4347. '-----------------------------------------------------------
  4348. ' FUNCTION: strExtractFilenameArg
  4349. '
  4350. ' Extracts a quoted or unquoted filename from a string
  4351. '   containing command-line arguments
  4352. '
  4353. ' IN: [str] - string containing a filename.  This filename
  4354. '             begins at the first character, and continues
  4355. '             to the end of the string or to the first space
  4356. '             or switch character, or, if the string begins
  4357. '             with a double quote, continues until the next
  4358. '             double quote
  4359. ' OUT: Returns the filename, without quotes
  4360. '      str is set to be the remainder of the string after
  4361. '      the filename and quote (if any)
  4362. '-----------------------------------------------------------
  4363. '
  4364. Private Function strExtractFilenameArg(str As String, fErr As Boolean)
  4365.     Dim strFilename As String
  4366.     Dim iEndFilenamePos As Integer
  4367.     
  4368.     Dim iSpacePos As Integer
  4369.     Dim iSwitch1 As Integer
  4370.     Dim iSwitch2 As Integer
  4371.     Dim iQuote As Integer
  4372.  
  4373.     str = Trim$(str)
  4374.     
  4375.     If Left$(str, 1) = gstrQUOTE Then
  4376.         ' Filenames is surrounded by quotes
  4377.         iEndFilenamePos = InStr(2, str, gstrQUOTE) ' Find matching quote
  4378.         If iEndFilenamePos > 0 Then
  4379.             strFilename = Mid$(str, 2, iEndFilenamePos - 2)
  4380.             str = Right$(str, Len(str) - iEndFilenamePos)
  4381.         Else
  4382.             fErr = True
  4383.             Exit Function
  4384.         End If
  4385.     Else
  4386.         ' Filename continues until next switch or space or quote
  4387.         iSpacePos = InStr(str, " ")
  4388.         iSwitch2 = InStr(str, gstrSwitchPrefix2)
  4389.         iQuote = InStr(str, gstrQUOTE)
  4390.         
  4391.         If iSpacePos = 0 Then iSpacePos = Len(str) + 1
  4392.         If iSwitch1 = 0 Then iSwitch1 = Len(str) + 1
  4393.         If iSwitch2 = 0 Then iSwitch2 = Len(str) + 1
  4394.         If iQuote = 0 Then iQuote = Len(str) + 1
  4395.         
  4396.         iEndFilenamePos = iSpacePos
  4397.         If iSwitch2 < iEndFilenamePos Then iEndFilenamePos = iSwitch2
  4398.         If iQuote < iEndFilenamePos Then iEndFilenamePos = iQuote
  4399.         
  4400.         strFilename = Left$(str, iEndFilenamePos - 1)
  4401.         If iEndFilenamePos > Len(str) Then
  4402.             str = vbNullString
  4403.         Else
  4404.             str = Right$(str, Len(str) - iEndFilenamePos + 1)
  4405.         End If
  4406.     End If
  4407.     
  4408.     strFilename = Trim$(strFilename)
  4409.     If Len(strFilename) = 0 Then
  4410.         fErr = True
  4411.         Exit Function
  4412.     End If
  4413.     
  4414.     fErr = False
  4415.     strExtractFilenameArg = strFilename
  4416.     str = Trim$(str)
  4417. End Function
  4418.  
  4419.  
  4420.  
  4421. '-----------------------------------------------------------
  4422. ' SUB: UpdateStatus
  4423. '
  4424. ' "Fill" (by percentage) inside the PictureBox and also
  4425. ' display the percentage filled
  4426. '
  4427. ' IN: [pic] - PictureBox used to bound "fill" region
  4428. '     [sngPercent] - Percentage of the shape to fill
  4429. '     [fBorderCase] - Indicates whether the percentage
  4430. '        specified is a "border case", i.e. exactly 0%
  4431. '        or exactly 100%.  Unless fBorderCase is True,
  4432. '        the values 0% and 100% will be assumed to be
  4433. '        "close" to these values, and 1% and 99% will
  4434. '        be used instead.
  4435. '
  4436. ' Notes: Set AutoRedraw property of the PictureBox to True
  4437. '        so that the status bar and percentage can be auto-
  4438. '        matically repainted if necessary
  4439. '-----------------------------------------------------------
  4440. '
  4441. Public Sub UpdateStatus(pic As PictureBox, ByVal sngPercent As Single, Optional ByVal fBorderCase As Boolean = False)
  4442.     Dim strPercent As String
  4443.     Dim intX As Integer
  4444.     Dim intY As Integer
  4445.     Dim intWidth As Integer
  4446.     Dim intHeight As Integer
  4447.  
  4448.     Dim intPercent As Integer
  4449.  
  4450.     'For this to work well, we need a white background and any color foreground (blue)
  4451.     Const colBackground = &HFFFFFF ' white
  4452.     Const colForeground = &H800000 ' dark blue
  4453.  
  4454.     pic.ForeColor = colForeground
  4455.     pic.BackColor = colBackground
  4456.     
  4457.     '
  4458.     'Format$ percentage and get attributes of text
  4459.     '
  4460.     intPercent = Int(100 * sngPercent + 0.5)
  4461.     
  4462.     'Never allow the percentage to be 0 or 100 unless it is exactly that value.  This
  4463.     'prevents, for instance, the status bar from reaching 100% until we are entirely done.
  4464.     If intPercent = 0 Then
  4465.         If Not fBorderCase Then
  4466.             intPercent = 1
  4467.         End If
  4468.     ElseIf intPercent = 100 Then
  4469.         If Not fBorderCase Then
  4470.             intPercent = 99
  4471.         End If
  4472.     End If
  4473.     
  4474.     strPercent = Format$(intPercent) & "%"
  4475.     intWidth = pic.TextWidth(strPercent)
  4476.     intHeight = pic.TextHeight(strPercent)
  4477.  
  4478.     '
  4479.     'Now set intX and intY to the starting location for printing the percentage
  4480.     '
  4481.     intX = pic.Width / 2 - intWidth / 2
  4482.     intY = pic.Height / 2 - intHeight / 2
  4483.  
  4484.     '
  4485.     'Need to draw a filled box with the pics background color to wipe out previous
  4486.     'percentage display (if any)
  4487.     '
  4488.     pic.DrawMode = 13 ' Copy Pen
  4489.     pic.Line (intX, intY)-Step(intWidth, intHeight), pic.BackColor, BF
  4490.  
  4491.     '
  4492.     'Back to the center print position and print the text
  4493.     '
  4494.     pic.CurrentX = intX
  4495.     pic.CurrentY = intY
  4496.     pic.Print strPercent
  4497.  
  4498.     '
  4499.     'Now fill in the box with the ribbon color to the desired percentage
  4500.     'If percentage is 0, fill the whole box with the background color to clear it
  4501.     'Use the "Not XOR" pen so that we change the color of the text to white
  4502.     'wherever we touch it, and change the color of the background to blue
  4503.     'wherever we touch it.
  4504.     '
  4505.     pic.DrawMode = 10 ' Not XOR Pen
  4506.     If sngPercent > 0 Then
  4507.         pic.Line (0, 0)-(pic.Width * sngPercent, pic.Height), pic.ForeColor, BF
  4508.     Else
  4509.         pic.Line (0, 0)-(pic.Width, pic.Height), pic.BackColor, BF
  4510.     End If
  4511.  
  4512.     pic.Refresh
  4513. End Sub
  4514.  
  4515. '-----------------------------------------------------------
  4516. ' FUNCTION: WriteAccess
  4517. '
  4518. ' Determines whether there is write access to the specified
  4519. ' directory.
  4520. '
  4521. ' IN: [strDirName] - directory to check for write access
  4522. '
  4523. ' Returns: True if write access, False otherwise
  4524. '-----------------------------------------------------------
  4525. '
  4526. Public Function WriteAccess(ByVal strDirName As String) As Boolean
  4527.     Const strFILE$ = "VB6STTMP.CCT"
  4528.  
  4529.     Dim intFileNum As Integer
  4530.  
  4531.     On Error Resume Next
  4532.  
  4533.     AddDirSep strDirName
  4534.  
  4535.     intFileNum = FreeFile
  4536.     Open strDirName & strFILE For Output As intFileNum
  4537.  
  4538.     WriteAccess = (Err.Number = 0)
  4539.  
  4540.     Close intFileNum
  4541.  
  4542.     Kill strDirName & strFILE
  4543.  
  4544.     Err.Clear
  4545. End Function
  4546. '-----------------------------------------------------------
  4547. ' FUNCTION: WriteMIF
  4548. '
  4549. ' If this is a SMS install, this routine writes the
  4550. ' failed MIF status file if something goes wrong or
  4551. ' a successful MIF if everything installs correctly.
  4552. '
  4553. ' The MIF file requires a special format specified
  4554. ' by SMS.  Currently, this routine implements the
  4555. ' minimum requirements.  The hardcoded strings below
  4556. ' that are written to the MIF should be written
  4557. ' character by character as they are; except that
  4558. ' status message should change depending on the
  4559. ' circumstances of the install.  DO NOT LOCALIZE
  4560. ' anything except the status message.
  4561. '
  4562. ' IN: [strMIFFilename] - The name of the MIF file.
  4563. '                        Passed in to setup1 by
  4564. '                        setup.exe.  It is probably
  4565. '                        named <appname>.mif where
  4566. '                        <appname> is the name of the
  4567. '                        application you are installing.
  4568. '
  4569. '     [fStatus] - False to write a failed MIF (i.e. setup
  4570. '                 failed); True to write a successful MIF.
  4571. '
  4572. '     [strSMSDescription] - This is the description string
  4573. '                           to be written to the MIF file.
  4574. '                           It cannot be longer than 255
  4575. '                           characters and cannot contain
  4576. '                           carriage returns and/or line
  4577. '                           feeds.  This routine will
  4578. '                           enforce these requirements.
  4579. '
  4580. ' Note, when running in SMS mode, there is no other way
  4581. ' to display a message to the user than to write it to
  4582. ' the MIF file.  Displaying a MsgBox will cause the
  4583. ' computer to appear as if it has hung.  Therefore, this
  4584. ' routine makes no attempt to display an error message.
  4585. '-----------------------------------------------------------
  4586. '
  4587. #If SMS Then
  4588. Private Sub WriteMIF(ByVal strMIFFilename As String, ByVal fStatus As Boolean, ByVal strSMSDescription As String)
  4589.     Const strSUCCESS = """SUCCESS"""                 ' Cannot be localized as per SMS
  4590.     Const strFAILED = """FAILED"""                   ' Cannot be localized as per SMS
  4591.     
  4592.     Dim fn As Integer
  4593.     Dim intOffset As Integer
  4594.     Dim fOpened As Boolean
  4595.         
  4596.     On Error GoTo WMIFFAILED  ' If we fail, we just return without doing anything
  4597.                               ' because there is no way to inform the user while
  4598.                               ' in SMS mode.
  4599.  
  4600.     '
  4601.     ' If the description string is greater than 255 characters,
  4602.     ' truncate it.  Required my SMS.
  4603.     '
  4604.     strSMSDescription = Left$(strSMSDescription, MAX_SMS_DESCRIP)
  4605.     '
  4606.     ' Remove any carriage returns or line feeds and replace
  4607.     ' them with spaces.  The message must be a single line.
  4608.     '
  4609.     For intOffset = 1 To Len(strSMSDescription)
  4610.         If (Mid$(strSMSDescription, intOffset, 1) = vbLf) Or (Mid$(strSMSDescription, intOffset, 1) = vbCr) Then
  4611.             Mid$(strSMSDescription, intOffset, 1) = " "
  4612.         End If
  4613.     Next intOffset
  4614.     '
  4615.     ' Open the MIF file for append, but first delete any existing
  4616.     ' ones with the same name.  Note, that setup.exe passed a
  4617.     ' unique name so if there is one with this name already in
  4618.     ' on the disk, it was put there by setup.exe.
  4619.     '
  4620.     If FileExists(strMIFFilename) Then
  4621.         Kill strMIFFilename
  4622.     End If
  4623.     
  4624.     fn = FreeFile
  4625.     Open strMIFFilename For Append As fn
  4626.     fOpened = True
  4627.     '
  4628.     ' We are ready to write the actual MIF file
  4629.     ' Note, none of the string below are supposed
  4630.     ' to be localized.
  4631.     '
  4632.     Print #fn, "Start Component"
  4633.         Print #fn, Tab; "Name = ""Workstation"""
  4634.         Print #fn, Tab; "Start Group"
  4635.             Print #fn, Tab; Tab; "Name = ""InstallStatus"""
  4636.             Print #fn, Tab; Tab; "ID = 1"
  4637.             Print #fn, Tab; Tab; "Class = ""MICROSOFT|JOBSTATUS|1.0"""
  4638.             Print #fn, Tab; Tab; "Start Attribute"
  4639.                 Print #fn, Tab; Tab; Tab; "Name = ""Status"""
  4640.                 Print #fn, Tab; Tab; Tab; "ID = 1"
  4641.                 Print #fn, Tab; Tab; Tab; "Type = String(16)"
  4642.                 If fStatus Then
  4643.                     Print #fn, Tab; Tab; Tab; "Value = "; strSUCCESS
  4644.                 Else
  4645.                     Print #fn, Tab; Tab; Tab; "Value = "; strFAILED
  4646.                 End If
  4647.             Print #fn, Tab; Tab; "End Attribute"
  4648.             Print #fn, Tab; Tab; "Start Attribute"
  4649.                 Print #fn, Tab; Tab; Tab; "Name = ""Description"""
  4650.                 Print #fn, Tab; Tab; Tab; "ID = 2"
  4651.                 Print #fn, Tab; Tab; Tab; "Type = String(256)"
  4652.                 Print #fn, Tab; Tab; Tab; "Value = "; strSMSDescription
  4653.             Print #fn, Tab; Tab; "End Attribute"
  4654.         Print #fn, Tab; "End Group"
  4655.     Print #fn, "End Component"
  4656.  
  4657.     Close fn
  4658.     '
  4659.     ' Success
  4660.     '
  4661.     Exit Sub
  4662.  
  4663. WMIFFAILED:
  4664.     '
  4665.     ' At this point we are unable to create the MIF file.
  4666.     ' Since we are running under SMS there is no one to
  4667.     ' tell, so we don't generate an error message at all.
  4668.     '
  4669.     If fOpened Then
  4670.         Close fn
  4671.     End If
  4672.     Exit Sub
  4673. End Sub
  4674. #End If
  4675.  
  4676. '-----------------------------------------------------------
  4677. 'Adds or replaces an HKEY to the list of HKEYs in cache.
  4678. 'Note that it is not necessary to remove keys from
  4679. 'this list.
  4680. '-----------------------------------------------------------
  4681. '
  4682. Private Sub AddHkeyToCache(ByVal hKey As Long, ByVal strHkey As String)
  4683.     Dim intIdx As Integer
  4684.     
  4685.     intIdx = intGetHKEYIndex(hKey)
  4686.     If intIdx < 0 Then
  4687.         'The key does not already exist.  Add it to the end.
  4688.         On Error Resume Next
  4689.         ReDim Preserve hkeyCache(0 To UBound(hkeyCache) + 1)
  4690.         If Err.Number <> 0 Then
  4691.             'If there was an error, it means the cache was empty.
  4692.             On Error GoTo 0
  4693.             ReDim hkeyCache(0 To 0)
  4694.         End If
  4695.         On Error GoTo 0
  4696.  
  4697.         intIdx = UBound(hkeyCache)
  4698.     Else
  4699.         'The key already exists.  It will be replaced.
  4700.     End If
  4701.  
  4702.     hkeyCache(intIdx).hKey = hKey
  4703.     hkeyCache(intIdx).strHkey = strHkey
  4704. End Sub
  4705.  
  4706. '-----------------------------------------------------------
  4707. 'Given a predefined HKEY, return the text string representing that
  4708. 'key, or else return vbNullString.
  4709. '-----------------------------------------------------------
  4710. '
  4711. Private Function strGetPredefinedHKEYString(ByVal hKey As Long) As String
  4712.     Select Case hKey
  4713.         Case HKEY_CLASSES_ROOT
  4714.             strGetPredefinedHKEYString = "HKEY_CLASSES_ROOT"
  4715.         Case HKEY_CURRENT_USER
  4716.             strGetPredefinedHKEYString = "HKEY_CURRENT_USER"
  4717.         Case HKEY_LOCAL_MACHINE
  4718.             strGetPredefinedHKEYString = "HKEY_LOCAL_MACHINE"
  4719.         Case HKEY_USERS
  4720.             strGetPredefinedHKEYString = "HKEY_USERS"
  4721.     End Select
  4722. End Function
  4723.  
  4724. '-----------------------------------------------------------
  4725. 'Given an HKEY, return the text string representing that
  4726. 'key.
  4727. '-----------------------------------------------------------
  4728. '
  4729. Private Function strGetHKEYString(ByVal hKey As Long) As String
  4730.     Dim strKey As String
  4731.     Dim intIdx As Integer
  4732.  
  4733.     'Is the hkey predefined?
  4734.     strKey = strGetPredefinedHKEYString(hKey)
  4735.     If Len(strKey) > 0 Then
  4736.         strGetHKEYString = strKey
  4737.         Exit Function
  4738.     End If
  4739.     
  4740.     'It is not predefined.  Look in the cache.
  4741.     intIdx = intGetHKEYIndex(hKey)
  4742.     If intIdx >= 0 Then
  4743.         strGetHKEYString = hkeyCache(intIdx).strHkey
  4744.     End If
  4745. End Function
  4746.  
  4747. '-----------------------------------------------------------
  4748. 'Searches the cache for the index of the given HKEY.
  4749. 'Returns the index if found, else returns -1.
  4750. '-----------------------------------------------------------
  4751. '
  4752. Private Function intGetHKEYIndex(ByVal hKey As Long) As Integer
  4753.     Dim intUBound As Integer
  4754.     Dim intIdx As Integer
  4755.     
  4756.     On Error Resume Next
  4757.     intUBound = UBound(hkeyCache)
  4758.     If Err.Number <> 0 Then
  4759.         'If there was an error accessing the ubound of the array,
  4760.         'then the cache is empty
  4761.         GoTo NotFound
  4762.     End If
  4763.     On Error GoTo 0
  4764.  
  4765.     For intIdx = 0 To intUBound
  4766.         If hkeyCache(intIdx).hKey = hKey Then
  4767.             intGetHKEYIndex = intIdx
  4768.             Exit Function
  4769.         End If
  4770.     Next intIdx
  4771.     
  4772. NotFound:
  4773.     intGetHKEYIndex = -1
  4774. End Function
  4775.  
  4776. '-----------------------------------------------------------
  4777. 'Returns the location of the Program Files\Common Files path, if
  4778. 'it is present in the registry.  Otherwise, returns vbNullString.
  4779. '-----------------------------------------------------------
  4780. '
  4781. Public Function strGetCommonFilesPath() As String
  4782.     Dim hKey As Long
  4783.     Dim strPath As String
  4784.     
  4785.     Const strCommonFilesKey = "CommonFilesDir"
  4786.  
  4787.     If RegOpenKey(HKEY_LOCAL_MACHINE, RegPathWinCurrentVersion(), hKey) Then
  4788.         RegQueryStringValue hKey, strCommonFilesKey, strPath
  4789.         RegCloseKey hKey
  4790.     End If
  4791.  
  4792.     If Len(strPath) > 0 Then
  4793.         AddDirSep strPath
  4794.     End If
  4795.     
  4796.     strGetCommonFilesPath = strPath
  4797. End Function
  4798.  
  4799. '-----------------------------------------------------------
  4800. 'Returns the location of the "Windows\Start Menu\Programs" Files path, if
  4801. 'it is present in the registry.  Otherwise, returns vbNullString.
  4802. '-----------------------------------------------------------
  4803. '
  4804. Public Function strGetProgramsFilesPath() As String
  4805.     Dim hKey As Long
  4806.     Dim strPath As String
  4807.     
  4808.     Const strProgramsKey = "Programs"
  4809.  
  4810.     If RegOpenKey(HKEY_CURRENT_USER, RegPathWinPrograms(), hKey) Then
  4811.         RegQueryStringValue hKey, strProgramsKey, strPath
  4812.         RegCloseKey hKey
  4813.     End If
  4814.  
  4815.     If Len(strPath) > 0 Then
  4816.         AddDirSep strPath
  4817.     End If
  4818.     
  4819.     strGetProgramsFilesPath = strPath
  4820. End Function
  4821.  
  4822. '-----------------------------------------------------------
  4823. 'Returns the directory where DAO is or should be installed.  If the
  4824. 'key does not exist in the registry, it is created.  For instance, under
  4825. 'NT 3.51 this location is normally 'C:\WINDOWS\MSAPPS\DAO'
  4826. '-----------------------------------------------------------
  4827. '
  4828. Private Function strGetDAOPath() As String
  4829.     Const strMSAPPS$ = "MSAPPS\"
  4830.     Const strDAO3032$ = "DAO350.DLL"
  4831.     
  4832.     'first look in the registry
  4833.     Const strKey = "SOFTWARE\Microsoft\Shared Tools\DAO350"
  4834.     Const strValueName = "Path"
  4835.     Dim hKey As Long
  4836.     Dim strPath As String
  4837.  
  4838.     If RegOpenKey(HKEY_LOCAL_MACHINE, strKey, hKey) Then
  4839.         RegQueryStringValue hKey, strValueName, strPath
  4840.         RegCloseKey hKey
  4841.     End If
  4842.  
  4843.     If Len(strPath) > 0 Then
  4844.         SeparatePathAndFileName strPath, strPath
  4845.         AddDirSep strPath
  4846.         strGetDAOPath = strPath
  4847.         Exit Function
  4848.     End If
  4849.     
  4850.     'It's not yet in the registry, so we need to decide
  4851.     'where the directory should be, and then need to place
  4852.     'that location in the registry.
  4853.  
  4854.     'For Win95, use "Common Files\Microsoft Shared\DAO"
  4855.     strPath = strGetCommonFilesPath() & ResolveResString(resMICROSOFTSHARED) & "DAO\"
  4856.     
  4857.     'Place this information in the registry (note that we point to DAO3032.DLL
  4858.     'itself, not just to the directory)
  4859.     If RegCreateKey(HKEY_LOCAL_MACHINE, strKey, vbNullString, hKey) Then
  4860.         RegSetStringValue hKey, strValueName, strPath & strDAO3032, False
  4861.         RegCloseKey hKey
  4862.     End If
  4863.  
  4864.     strGetDAOPath = strPath
  4865. End Function
  4866.  
  4867. '-----------------------------------------------------------
  4868. ' Replace all double quotes with single quotes
  4869. '-----------------------------------------------------------
  4870. '
  4871. Public Sub ReplaceDoubleQuotes(str As String)
  4872.     Dim i As Integer
  4873.     
  4874.     For i = 1 To Len(str)
  4875.         If Mid$(str, i, 1) = gstrQUOTE Then
  4876.             Mid$(str, i, 1) = "'"
  4877.         End If
  4878.     Next i
  4879. End Sub
  4880.  
  4881. '-----------------------------------------------------------
  4882. 'Returns the path to the root of the first fixed disk
  4883. '-----------------------------------------------------------
  4884. '
  4885. Private Function strRootDrive() As String
  4886.     Dim intDriveNum As Integer
  4887.     
  4888.     For intDriveNum = 0 To Asc("Z") - Asc("A")
  4889.         If GetDriveType(intDriveNum) = intDRIVE_FIXED Then
  4890.             strRootDrive = Chr$(Asc("A") + intDriveNum) & gstrCOLON & gstrSEP_DIR
  4891.             Exit Function
  4892.         End If
  4893.     Next intDriveNum
  4894.  
  4895.     strRootDrive = "C:\"
  4896. End Function
  4897.  
  4898. '-----------------------------------------------------------
  4899. ' This routine verifies that strFileName is a valid file name.
  4900. ' It checks that its length is less than the max allowed
  4901. ' and that it doesn't contain any invalid characters..
  4902. '-----------------------------------------------------------
  4903. '
  4904. Public Function fValidFilename(strFilename As String) As Boolean
  4905.     Dim iInvalidChar As Integer
  4906.     Dim iFilename As Integer
  4907.     Dim strInvalidChars As String
  4908.  
  4909.     If Not fCheckFNLength(strFilename) Then
  4910.         '
  4911.         ' Name is too long.
  4912.         '
  4913.         Exit Function
  4914.     End If
  4915.     '
  4916.     ' Search through the list of invalid filename characters and make
  4917.     ' sure none of them are in the string.
  4918.     '
  4919.     strInvalidChars = ResolveResString(resCOMMON_INVALIDFILECHARS)
  4920.     
  4921.     For iInvalidChar = 1 To Len(strInvalidChars)
  4922.         If InStr(strFilename, Mid$(strInvalidChars, iInvalidChar, 1)) <> 0 Then
  4923.             Exit Function
  4924.         End If
  4925.     Next iInvalidChar
  4926.     
  4927.     fValidFilename = True
  4928. End Function
  4929.  
  4930. '-----------------------------------------------------------
  4931. ' SUB: CountGroups
  4932. '
  4933. ' Determines how many groups must be installed by counting
  4934. ' them in the setup information file (SETUP.LST)
  4935. '-----------------------------------------------------------
  4936. '
  4937. Public Function CountGroups(ByVal strSection As String) As Integer
  4938.     Dim intIdx As Integer
  4939.     Dim sGroup As String
  4940.     
  4941.     intIdx = 0
  4942.     Do
  4943.         sGroup = ReadIniFile(gstrSetupInfoFile, strSection, gsGROUP & CStr(intIdx))
  4944.         If Len(sGroup) > 0 Then 'Found a group
  4945.             intIdx = intIdx + 1
  4946.         Else
  4947.             Exit Do
  4948.         End If
  4949.     Loop
  4950.     CountGroups = intIdx
  4951. End Function
  4952. '-----------------------------------------------------------
  4953. ' SUB: GetGroup
  4954. '
  4955. ' Returns the Groupname specified by Index
  4956. '-----------------------------------------------------------
  4957. '
  4958. Public Function GetGroup(ByVal strSection As String, ByVal Index As Integer)
  4959.     GetGroup = ReadIniFile(gstrSetupInfoFile, strSection, gsGROUP & CStr(Index))
  4960. End Function
  4961.  
  4962. '-----------------------------------------------------------
  4963. ' SUB: SetGroup
  4964. '
  4965. ' Sets Groupname specified by Index
  4966. '-----------------------------------------------------------
  4967. '
  4968. Public Sub SetGroup(ByVal strSection As String, ByVal Index As Integer, ByVal sGroupName As String)
  4969.     Const iBuf As Integer = 2048
  4970.     Const sEQUAL As String * 1 = "="
  4971.     Dim sGroup As String
  4972.     Dim sNames As String
  4973.     Dim sChar As String
  4974.     Dim ret As Long
  4975.     Dim lCount As Long
  4976.     Dim sKEY As String
  4977.     Dim sValue As String
  4978.     Dim fKey As Boolean
  4979.  
  4980.     sGroup = ReadIniFile(gstrSetupInfoFile, strSection, gsGROUP & CStr(Index))
  4981.     sNames = Space$(iBuf)
  4982.     ret = GetPrivateProfileSection(sGroup, sNames, iBuf, gstrSetupInfoFile)
  4983.     If ret = 0 Then 'We have nothing in this section, just quit.
  4984.         Exit Sub
  4985.     End If
  4986.     sNames = Left$(sNames, InStr(sNames, vbNullChar & vbNullChar))
  4987.     'We now have the Group name, modify the icons in that group
  4988.     fKey = True
  4989.     For lCount = 1 To Len(sNames)
  4990.         sChar = Mid$(sNames, lCount, 1)
  4991.         If (sChar = sEQUAL) Then
  4992.             fKey = False
  4993.         ElseIf (Asc(sChar) = 0) Or (Len(sNames) = lCount) Then
  4994.             If Len(sNames) = lCount Then
  4995.                 If fKey Then
  4996.                     sKEY = sKEY & sChar
  4997.                 Else
  4998.                     sValue = sValue & sChar
  4999.                 End If
  5000.             End If
  5001.             If Len(sKEY) <> 0 Then
  5002.                 WritePrivateProfileString sGroupName, sKEY, sValue, gstrSetupInfoFile
  5003.             End If
  5004.             sKEY = vbNullString
  5005.             sValue = vbNullString
  5006.             fKey = True
  5007.         Else
  5008.             If fKey Then
  5009.                 sKEY = sKEY & sChar
  5010.             Else
  5011.                 sValue = sValue & sChar
  5012.             End If
  5013.         End If
  5014.     Next
  5015.     WritePrivateProfileString strSection, gsGROUP & CStr(Index), sGroupName, gstrSetupInfoFile
  5016. End Sub
  5017. '-----------------------------------------------------------
  5018. ' SUB: GetPrivate
  5019. '
  5020. ' Returns the the value of whether the group is private specified by Index
  5021. '-----------------------------------------------------------
  5022. '
  5023. Public Function GetPrivate(ByVal strSection As String, ByVal Index As Integer) As Boolean
  5024.     GetPrivate = CBool(ReadIniFile(gstrSetupInfoFile, strSection, gsPRIVATE & CStr(Index)))
  5025. End Function
  5026. Public Function GetStart(ByVal strSection As String, ByVal Index As Integer) As Boolean
  5027.     GetStart = UCase$(ReadIniFile(gstrSetupInfoFile, strSection, gsPARENT & CStr(Index))) = UCase$(gsSTARTMENUKEY)
  5028. End Function
  5029.  
  5030. '-----------------------------------------------------------
  5031. ' SUB: CountIcons
  5032. '
  5033. ' Determines how many icons must be installed by counting
  5034. ' them in the setup information file (SETUP.LST)
  5035. '-----------------------------------------------------------
  5036. '
  5037. Public Function CountIcons(ByVal strSection As String) As Integer
  5038.     Dim intIdx As Integer
  5039.     Dim cIcons As Integer
  5040.     Dim sGroup As String
  5041.     Dim oCol As New Collection
  5042.     Dim sGName As String
  5043.     Dim vGroup As Variant
  5044.  
  5045.     intIdx = 0
  5046.     cIcons = 0
  5047.     Do
  5048.         sGroup = ReadIniFile(gstrSetupInfoFile, strSection, gsGROUP & CStr(intIdx))
  5049.         If Len(sGroup) > 0 Then 'Found a group
  5050.             oCol.Add sGroup
  5051.             intIdx = intIdx + 1
  5052.         Else
  5053.             Exit Do
  5054.         End If
  5055.     Loop
  5056.     For Each vGroup In oCol
  5057.         intIdx = 1
  5058.         Do
  5059.             sGName = ReadIniFile(gstrSetupInfoFile, vGroup, gsICON & CStr(intIdx))
  5060.             If Len(sGName) > 0 Then
  5061.                 cIcons = cIcons + 1
  5062.                 intIdx = intIdx + 1
  5063.             Else
  5064.                 Exit Do
  5065.             End If
  5066.         Loop
  5067.     Next
  5068.     CountIcons = cIcons
  5069.     
  5070. End Function
  5071. '-----------------------------------------------------------
  5072. ' SUB: CreateIcons
  5073. '
  5074. ' Walks through the list of files in SETUP.LST and creates
  5075. ' Icons in the Program Group for files needed it.
  5076. '-----------------------------------------------------------
  5077. '
  5078. Public Sub CreateIcons(ByVal strSection As String)
  5079.     Dim intIdx As Integer
  5080.     Dim sFile As FILEINFO
  5081.     Dim strProgramIconTitle As String
  5082.     Dim strProgramIconCmdLine As String
  5083.     Dim strProgramPath As String
  5084.     Dim strProgramArgs As String
  5085.     Dim intAnchor As Integer
  5086.     Dim intOffset As Integer
  5087.     Dim strGroup As String
  5088.     Dim sGroup As String
  5089.     Dim oCol As New Collection
  5090.     Const CompareBinary = 0
  5091.     Dim sGName As String
  5092.     Dim vGroup As Variant
  5093.     Dim fPrivate As Boolean
  5094.     Dim sParent As String
  5095.     Dim intIdx2 As Integer
  5096.     '
  5097.     'For each file in the specified section, read info from the setup info file
  5098.     '
  5099.     intIdx = 0
  5100.     Do
  5101.         sGroup = ReadIniFile(gstrSetupInfoFile, strSection, gsGROUP & CStr(intIdx))
  5102.         If Len(sGroup) > 0 Then  'Found a group
  5103.             oCol.Add sGroup
  5104.             intIdx = intIdx + 1
  5105.         Else
  5106.             Exit Do
  5107.         End If
  5108.     Loop
  5109.     For Each vGroup In oCol
  5110.         intIdx = 0
  5111.         Do
  5112.             intIdx = intIdx + 1
  5113.             sGName = ReadIniFile(gstrSetupInfoFile, vGroup, gsICON & CStr(intIdx))
  5114.             If Len(sGName) > 0 Then
  5115.                 '
  5116.                 ' Get the Icon's caption and command line
  5117.                 '
  5118.                 strProgramIconTitle = ReadIniFile(gstrSetupInfoFile, vGroup, gsTITLE & CStr(intIdx))
  5119.                 strProgramIconCmdLine = ReadIniFile(gstrSetupInfoFile, vGroup, gsICON & CStr(intIdx))
  5120.                 strGroup = vGroup
  5121.                 '
  5122.                 ' if the ProgramIcon is specified, then we create an icon,
  5123.                 ' otherwise we don't.
  5124.                 '
  5125.                 If Len(Trim$(strUnQuoteString(strProgramIconTitle))) > 0 Then
  5126.                     '
  5127.                     ' If the command line is not specified in SETUP.LST and the icon
  5128.                     ' is, then use the files destination path as the command line.  In
  5129.                     ' this case there are no parameters.
  5130.                     '
  5131.                     If Len(Trim$(strUnQuoteString(strProgramIconCmdLine))) = 0 Then
  5132.                         strProgramPath = sFile.strDestDir & gstrSEP_DIR & sFile.strDestName
  5133.                         strProgramArgs = vbNullString
  5134.                     Else
  5135.                         '
  5136.                         ' Parse the command line, to determine what is the exe, etc. and what
  5137.                         ' are the parameters.  The first space that is not contained within
  5138.                         ' quotes, marks the end of the exe, etc..  Everything afterwards are
  5139.                         ' parameters/arguments for the exe.  NOTE: It is important that if
  5140.                         ' the exe is contained within quotes that the parameters not be
  5141.                         ' contained within the same quotes.  The arguments can themselves
  5142.                         ' each be inside quotes as long as they are not in the same quotes
  5143.                         ' with the exe.
  5144.                         '
  5145.                         intAnchor = 1
  5146.                         intOffset = intGetNextFldOffset(intAnchor, strProgramIconCmdLine, " ")
  5147.                         If intOffset = 0 Then intOffset = Len(strProgramIconCmdLine) + 1
  5148.                         strProgramPath = Trim$(Left$(strProgramIconCmdLine, intOffset - 1))
  5149.                         '
  5150.                         ' Got the exe, now the parameters.
  5151.                         '
  5152.                         strProgramArgs = Trim$(Mid$(strProgramIconCmdLine, intOffset + 1))
  5153.                     End If
  5154.                     '
  5155.                     ' Expand all the Destination Directory macros that are embedded in the
  5156.                     ' Program Path and the Arguments'
  5157.                     '
  5158.                     strProgramPath = ResolveDestDir(strProgramPath)
  5159.                     strProgramArgs = ResolveDestDirs(strProgramArgs)
  5160.                     '
  5161.                     ' Finally, we have everything we need, create the icon.
  5162.                     '
  5163.                     intIdx2 = 0
  5164.                     Do
  5165.                         sGroup = ReadIniFile(gstrSetupInfoFile, gsICONGROUP, gsGROUP & CStr(intIdx2))
  5166.                         If UCase$(sGroup) = UCase$(strGroup) Then 'Found the group
  5167.                             If IsWindows95 Then
  5168.                                 fPrivate = True
  5169.                             Else
  5170.                                 fPrivate = GetPrivate(gsICONGROUP, intIdx2)
  5171.                             End If
  5172.                             If GetStart(gsICONGROUP, intIdx2) Then
  5173.                                 sParent = gsSTARTMENUKEY
  5174.                             Else
  5175.                                 sParent = gsPROGMENUKEY
  5176.                             End If
  5177.                             Exit Do
  5178.                         End If
  5179.                         intIdx2 = intIdx2 + 1
  5180.                     Loop
  5181.                     CreateShellLink strProgramPath, strGroup, strProgramArgs, strProgramIconTitle, fPrivate, sParent
  5182.                 ElseIf Len(Trim$(strUnQuoteString(strProgramIconCmdLine))) > 0 Then
  5183.                     '
  5184.                     ' This file contained specified a command line in SETUP.LST but no icon.
  5185.                     ' This is an error.  Let the user know and skip this icon or abort.
  5186.         
  5187.                     '
  5188.                     If gfNoUserInput Or MsgWarning(ResolveResString(resICONMISSING, gstrPIPE1, sFile.strDestName), vbYesNo Or vbExclamation, gstrSETMSG) = vbNo Then
  5189.                         ExitSetup frmSetup1, gintRET_FATAL
  5190.                     End If
  5191.                 End If
  5192.             Else
  5193.                 Exit Do
  5194.             End If
  5195.         Loop
  5196.     Next
  5197. End Sub
  5198.  
  5199. '-----------------------------------------------------------
  5200. ' SUB: RebootSystem
  5201. '
  5202. ' Initiates a reboot. Returns immediately while reboot
  5203. ' proceeds. Returns True if reboot is successfully
  5204. ' initiated. Returns False otherwise.
  5205. '-----------------------------------------------------------
  5206. '
  5207. Public Function RebootSystem() As Boolean
  5208.     Dim ret As Long
  5209.     Dim hToken As Long
  5210.     Dim tkp As TOKEN_PRIVILEGES
  5211.     Dim tkpOld As TOKEN_PRIVILEGES
  5212.     Dim fOkReboot As Boolean
  5213.     Const sSHUTDOWN As String = "SeShutdownPrivilege"
  5214.  
  5215.     'Check to see if we are running on Windows NT
  5216.     If IsWindowsNT() Then
  5217.         'We are running windows NT.  We need to do some security checks/modifications
  5218.         'to ensure we have the token that allows us to reboot.
  5219.         If OpenProcessToken(GetCurrentProcess(), _
  5220.                 TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY, hToken) Then
  5221.             ret = LookupPrivilegeValue(vbNullString, sSHUTDOWN, tkp.Privileges(0).pLuid)
  5222.             tkp.PrivilegeCount = 1
  5223.             tkp.Privileges(0).Attributes = SE_PRIVILEGE_ENABLED
  5224.             fOkReboot = AdjustTokenPrivileges(hToken, 0, tkp, LenB(tkpOld), tkpOld, ret)
  5225.         End If
  5226.     Else
  5227.         'We are running Win95/98.  Nothing needs to be done.
  5228.         fOkReboot = True
  5229.     End If
  5230.     If fOkReboot Then RebootSystem = (ExitWindowsEx(EWX_REBOOT, 0) <> 0)
  5231. End Function
  5232.  
  5233. Private Function GetFileTime(ByVal aDate As Date) As FileTime
  5234.     Dim lTemp As SYSTEMTIME
  5235.     Dim lTime As FileTime
  5236.     
  5237.     VariantTimeToSystemTime aDate, lTemp
  5238.     SystemTimeToFileTime lTemp, lTime
  5239.     LocalFileTimeToFileTime lTime, GetFileTime
  5240. End Function
  5241.  
  5242. '-----------------------------------------------------------
  5243. ' SUB: HandleFormQueryUnload
  5244. '
  5245. ' Consolidates processing of Form_QueryUnload events. When a
  5246. ' user closes a form, we check and make sure they want to
  5247. ' exit. If yes, we shut down. If no, we cancel the unload.
  5248. '-----------------------------------------------------------
  5249. '
  5250. Public Sub HandleFormQueryUnload(UnloadMode As Integer, ByRef Cancel As Integer, Form As Form)
  5251.     If UnloadMode <> vbFormCode Then
  5252.         ExitSetup Form, gintRET_EXIT
  5253.         'If ExitSetup is cancelled, then we need to cancel the form unload.
  5254.         Cancel = True
  5255.     End If
  5256. End Sub
  5257.  
  5258. '-----------------------------------------------------------
  5259. ' SUB: CheckForAndInstallDirectX
  5260. '
  5261. ' Checks for a current version of DirectX and if it isn't the
  5262. ' latest, or not installed, then we should install our version.
  5263. ' Check only happens if the directx redist folder is part of the
  5264. ' package.
  5265. '-----------------------------------------------------------
  5266. '
  5267.  
  5268. Public Sub CheckForAndInstallDirectX(ByVal strSection As String, ByVal lhWnd As Long)
  5269.  
  5270.     
  5271.     Dim ret As Long, fFoundFiles As Boolean
  5272.     Dim lMajor As Long, lMinor As Long
  5273.     Dim ans As Long, fInstall As Boolean
  5274.     Dim sCurDir As String, sNewSrc As String
  5275.     Dim intIdx As Integer
  5276.     Dim sFile As FILEINFO
  5277.         
  5278.     'First we need to check to see if the DirectX redist files are in the package.
  5279.     fFoundFiles = False
  5280.     intIdx = 1
  5281.     Do While ReadSetupFileLine(strSection, intIdx, sFile) And Not fFoundFiles
  5282.         If IsFileADXRedistFile(sFile.strSrcName) Then
  5283.             'We found a dx redist file we're done
  5284.             fFoundFiles = True
  5285.         End If
  5286.         intIdx = intIdx + 1
  5287.     Loop
  5288.     
  5289.     If fFoundFiles Then
  5290.         If Not DirExists(gsTEMPDIR) Then 'Make sure the temp folder exists
  5291.             MkDir gsTEMPDIR
  5292.         End If
  5293.         'Extract all the rest of the files for the DX redist
  5294.         intIdx = 1
  5295.         Do While ReadSetupFileLine(strSection, intIdx, sFile)
  5296.             If IsFileADXRedistFile(sFile.strSrcName) Then
  5297.                 'Extract this file to the temp folder
  5298.                 sNewSrc = gsTEMPDIR & sFile.strDestName
  5299.                 ExtractFileFromCab gsCABFULLNAME, sFile.strSrcName, sNewSrc, gintCabs, gstrSrcPath
  5300.             End If
  5301.             intIdx = intIdx + 1
  5302.         Loop
  5303.         'Save the current Drive/Path information
  5304.         sCurDir = CurDir
  5305.         If (Left$(gsTEMPDIR, 2) <> Left$(CurDir, 2)) And (InStr(Left$(gsTEMPDIR, 2), ":") > 0) Then ChDrive Left$(gsTEMPDIR, 2)
  5306.         ChDir gsTEMPDIR
  5307.         fInstall = False 'Do not install by default
  5308.         ret = DirectXSetupGetVersion(lMajor, lMinor)
  5309.         If ret = 0 Then
  5310.             'The GetVersion call failed.
  5311.             'Here would be a good place to put a dialog
  5312.             'asking the end user if they want to install
  5313.             'DX8.  We will just install by default (if need be).
  5314.             fInstall = True
  5315.         Else
  5316.             lMajor = lMajor - (lMajor And DSETUP_VERSION)
  5317.             If lMajor < 8 Then
  5318.                 'Here would be a good place to put a dialog
  5319.                 'asking the end user if they want to install
  5320.                 'DX8.  We will just install by default (if need be).
  5321.                 fInstall = True
  5322.             End If
  5323.         End If
  5324.         If fInstall Then 'Install DX8
  5325.             ret = DirectXSetup(0, Left$(gsTEMPDIR, Len(gsTEMPDIR) - 1), DSETUP_DIRECTX)
  5326.             If ret = DSETUPERR_SUCCESS Then
  5327.                 'Success do nothing
  5328.             ElseIf ret = DSETUPERR_SUCCESS_RESTART Then
  5329.                 gfDXReboot = True
  5330.             Else
  5331.                 'There was an error installing DX8
  5332.                 'You may place any error handling (or information)
  5333.                 'routines you wish here.  We fail silently.
  5334.             End If
  5335.         End If
  5336.         If (Left$(sCurDir, 2) <> Left$(CurDir, 2)) And (InStr(Left$(sCurDir, 2), ":") > 0) Then ChDrive Left$(sCurDir, 2)
  5337.         ChDir sCurDir
  5338.     End If
  5339. End Sub
  5340.  
  5341. Private Function IsFileADXRedistFile(ByVal sFile As String) As Boolean
  5342.     IsFileADXRedistFile = False
  5343.     If UCase$(sFile) = UCase$(gstrAT & gstrFILE_DSETUP) Then
  5344.         IsFileADXRedistFile = True
  5345.     End If
  5346.     If UCase$(sFile) = UCase$(gstrAT & gstrFILE_DSETUP32) Then
  5347.         IsFileADXRedistFile = True
  5348.     End If
  5349.     If UCase$(sFile) = UCase$(gstrAT & gstrFILE_CFGMGR32) Then
  5350.         IsFileADXRedistFile = True
  5351.     End If
  5352.     If UCase$(sFile) = UCase$(gstrAT & gstrFILE_DIRECTXCAB) Then
  5353.         IsFileADXRedistFile = True
  5354.     End If
  5355.     If UCase$(sFile) = UCase$(gstrAT & gstrFILE_DXSETUP) Then
  5356.         IsFileADXRedistFile = True
  5357.     End If
  5358.     If UCase$(sFile) = UCase$(gstrAT & gstrFILE_SETUPAPIDLL) Then
  5359.         IsFileADXRedistFile = True
  5360.     End If
  5361.     If UCase$(sFile) = UCase$(gstrAT & gstrFILE_BDACAB) Then
  5362.         IsFileADXRedistFile = True
  5363.     End If
  5364.     If UCase$(sFile) = UCase$(gstrAT & gstrFILE_BDANTCAB) Then
  5365.         IsFileADXRedistFile = True
  5366.     End If
  5367.     If UCase$(sFile) = UCase$(gstrAT & gstrFILE_DXNTCAB) Then
  5368.         IsFileADXRedistFile = True
  5369.     End If
  5370.     If UCase$(sFile) = UCase$(gstrAT & gstrFILE_WAMSETUP) Then
  5371.         IsFileADXRedistFile = True
  5372.     End If
  5373.     
  5374. End Function
  5375.